pushary 1.9.93 → 1.9.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{api-QUARGXSY.js → api-QBIRIMGU.js} +10 -10
  3. package/dist/bin/pushary-bell-hook.js +26 -26
  4. package/dist/bin/pushary-bell.js +10 -10
  5. package/dist/bin/pushary-claude.js +27 -27
  6. package/dist/bin/pushary-clean.js +34 -34
  7. package/dist/bin/pushary-codex-bridge.js +21 -21
  8. package/dist/bin/pushary-codex-hook.js +34 -32
  9. package/dist/bin/pushary-codex-session.js +8 -8
  10. package/dist/bin/pushary-codex.js +14 -14
  11. package/dist/bin/pushary-connect.js +27 -27
  12. package/dist/bin/pushary-cowork.js +5 -5
  13. package/dist/bin/pushary-daemon.js +39 -39
  14. package/dist/bin/pushary-disconnect.js +26 -26
  15. package/dist/bin/pushary-display-hook.js +17 -17
  16. package/dist/bin/pushary-doctor.js +42 -42
  17. package/dist/bin/pushary-elicitation-hook.js +26 -26
  18. package/dist/bin/pushary-gemini-bridge.js +15 -15
  19. package/dist/bin/pushary-gemini-hook.js +28 -28
  20. package/dist/bin/pushary-hook.js +30 -30
  21. package/dist/bin/pushary-login.js +20 -20
  22. package/dist/bin/pushary-logout.js +25 -25
  23. package/dist/bin/pushary-mcp.js +11 -11
  24. package/dist/bin/pushary-mode.js +15 -15
  25. package/dist/bin/pushary-notification-hook.js +25 -25
  26. package/dist/bin/pushary-opencode-hook.js +27 -27
  27. package/dist/bin/pushary-permission-denied-hook.js +30 -30
  28. package/dist/bin/pushary-permission-hook.js +30 -30
  29. package/dist/bin/pushary-post-hook.js +25 -25
  30. package/dist/bin/pushary-prompt-hook.js +25 -25
  31. package/dist/bin/pushary-session-end-hook.js +27 -27
  32. package/dist/bin/pushary-session-start-hook.js +35 -35
  33. package/dist/bin/pushary-setup.js +48 -48
  34. package/dist/bin/pushary-stats.js +5 -5
  35. package/dist/bin/pushary-status.js +32 -32
  36. package/dist/bin/pushary-stop-hook.js +25 -25
  37. package/dist/bin/pushary-stopfailure-hook.js +25 -25
  38. package/dist/bin/pushary-suggestions.js +15 -15
  39. package/dist/bin/pushary-transcript-register.js +18 -18
  40. package/dist/bin/pushary-transcripts.js +26 -26
  41. package/dist/bin/pushary-upgrade.js +40 -40
  42. package/dist/bin/pushary-wait.js +15 -15
  43. package/dist/bin/pushary.js +7 -7
  44. package/dist/{chunk-OFQJHWAS.js → chunk-2TA5UKNE.js} +10 -10
  45. package/dist/{chunk-3CJFBWID.js → chunk-3DASOC3M.js} +3 -3
  46. package/dist/{chunk-7YB2XRY7.js → chunk-3GZCHFYV.js} +3 -3
  47. package/dist/{chunk-ANDG4PUV.js → chunk-4GNFBBE6.js} +6 -6
  48. package/dist/{chunk-2FL5PLA4.js → chunk-4N6MND55.js} +2 -2
  49. package/dist/{chunk-XDWJX7LT.js → chunk-4YPTEVZD.js} +3 -3
  50. package/dist/{chunk-7PQ66CB3.js → chunk-4YSFD4RY.js} +1 -1
  51. package/dist/{chunk-NZKH2RMQ.js → chunk-5M6ASRLB.js} +1 -1
  52. package/dist/{chunk-GLA6MCOG.js → chunk-5QSPRDUO.js} +1 -1
  53. package/dist/{chunk-ONRA5ON4.js → chunk-5ZBYW7IO.js} +1 -1
  54. package/dist/{chunk-RFP4Q5I3.js → chunk-6SN6L5YT.js} +11 -11
  55. package/dist/{chunk-4HQTZQ4D.js → chunk-6YX4VASU.js} +2 -2
  56. package/dist/{chunk-NPH4DJAU.js → chunk-7Y3AQNHV.js} +5 -5
  57. package/dist/{chunk-JZ2MRWC4.js → chunk-AEKTC2DX.js} +3 -3
  58. package/dist/{chunk-DV7LLH25.js → chunk-AMPF5FAS.js} +1 -1
  59. package/dist/{chunk-UGGACQMD.js → chunk-ANZECRZO.js} +3 -3
  60. package/dist/{chunk-RXAQOAZI.js → chunk-AUIRBCYE.js} +3 -3
  61. package/dist/{chunk-ATWH6TGY.js → chunk-BE4KMA7N.js} +18 -15
  62. package/dist/{chunk-UHS3JZLN.js → chunk-BOHUB5VP.js} +2 -2
  63. package/dist/{chunk-CFD3IESN.js → chunk-CA2V7TKX.js} +6 -6
  64. package/dist/{chunk-4PD7FP55.js → chunk-CDIVGMBR.js} +2 -2
  65. package/dist/{chunk-MSJXRN22.js → chunk-CS2C7S5L.js} +5 -5
  66. package/dist/{chunk-HUNGCBCF.js → chunk-E2WYLZIL.js} +3 -3
  67. package/dist/{chunk-SPPMPM7F.js → chunk-EFCHGKAD.js} +1 -1
  68. package/dist/{chunk-CQMY74I2.js → chunk-ERQD5UVH.js} +7 -7
  69. package/dist/{chunk-PS2K6ZQ3.js → chunk-F74RBPJY.js} +6 -6
  70. package/dist/{chunk-NDPOESVW.js → chunk-FQZSKWWL.js} +1 -1
  71. package/dist/{chunk-3JVV6PMG.js → chunk-FSMR6GJT.js} +5 -5
  72. package/dist/{chunk-Y7AX6BS4.js → chunk-GPGLRSGY.js} +3 -3
  73. package/dist/{chunk-7VJ7PEI3.js → chunk-HHE7ONVT.js} +4 -4
  74. package/dist/{chunk-A2FDS7E3.js → chunk-HHKBWPXA.js} +5 -5
  75. package/dist/{chunk-7GFWYFLO.js → chunk-HRFOVTCQ.js} +10 -10
  76. package/dist/{chunk-FTJ5DQL7.js → chunk-HZQIC3DG.js} +2 -2
  77. package/dist/{chunk-NPZ5L2WQ.js → chunk-IB5MLSE3.js} +1 -1
  78. package/dist/{chunk-CSUO5YJG.js → chunk-IMNXYNBJ.js} +2 -2
  79. package/dist/{chunk-76ROCOWD.js → chunk-JCZ7WE5G.js} +7 -7
  80. package/dist/{chunk-N7CPX5S5.js → chunk-JJ2AA3N6.js} +5 -5
  81. package/dist/{chunk-VMIWR724.js → chunk-JLWI2HZF.js} +1 -1
  82. package/dist/{chunk-OLMKZINV.js → chunk-K3E3NFTT.js} +6 -6
  83. package/dist/{chunk-5V2YDO4U.js → chunk-K56A3LSA.js} +4 -4
  84. package/dist/{chunk-X3AHEUQ5.js → chunk-KH3QOWTE.js} +3 -3
  85. package/dist/{chunk-KJ752DUH.js → chunk-KXCSF2IK.js} +6 -6
  86. package/dist/{chunk-CHVXLS7U.js → chunk-LFIFIIMK.js} +2 -2
  87. package/dist/{chunk-NHGOCUX7.js → chunk-MC7I3SZW.js} +1 -1
  88. package/dist/{chunk-7DOSRSRU.js → chunk-OSDL6OND.js} +6 -6
  89. package/dist/{chunk-XTNCXIYR.js → chunk-OWCWJBGW.js} +4 -4
  90. package/dist/{chunk-BETKU76V.js → chunk-PGCDYBKG.js} +3 -3
  91. package/dist/{chunk-2BVVMWVF.js → chunk-QNFDU72D.js} +1 -1
  92. package/dist/{chunk-YLCNW7YN.js → chunk-QR7RXGNF.js} +2 -2
  93. package/dist/{chunk-LTMMFZHG.js → chunk-QU2G6MM7.js} +1 -1
  94. package/dist/{chunk-66VEUAN5.js → chunk-QVCDU4NT.js} +1 -1
  95. package/dist/{chunk-YT3ENXBT.js → chunk-QYP4EEDA.js} +2 -2
  96. package/dist/{chunk-UZRKR6NZ.js → chunk-RCDCFFG4.js} +3 -3
  97. package/dist/{chunk-LANGH7UF.js → chunk-REV2QMUD.js} +4 -4
  98. package/dist/{chunk-V3LBQFQM.js → chunk-RGMS5EDS.js} +1 -1
  99. package/dist/{chunk-SR44ZNZF.js → chunk-SKYDYDGX.js} +5 -5
  100. package/dist/{chunk-JJWZIPDL.js → chunk-SSXMA23D.js} +4 -4
  101. package/dist/{chunk-H4MIOBC6.js → chunk-SV6WE5QB.js} +2 -2
  102. package/dist/{chunk-V7TNHTHH.js → chunk-TYHOBIDF.js} +1 -1
  103. package/dist/{chunk-5TQC2LS6.js → chunk-UC6IZRXP.js} +4 -4
  104. package/dist/{chunk-2BXWBKVN.js → chunk-UFT4R4V7.js} +3 -3
  105. package/dist/{chunk-OC47FGOY.js → chunk-VDBRLNW7.js} +1 -1
  106. package/dist/{chunk-373552A3.js → chunk-X5DCL36E.js} +4 -4
  107. package/dist/{chunk-HALVTC2N.js → chunk-X7GKDLW7.js} +5 -5
  108. package/dist/{chunk-WW4OMRZX.js → chunk-XASOD3XX.js} +1 -1
  109. package/dist/{chunk-OMN5UCMW.js → chunk-XERTD7VZ.js} +520 -40
  110. package/dist/{chunk-CO6TQ44W.js → chunk-YQCRX4T6.js} +3 -3
  111. package/dist/{chunk-RXQT5FTX.js → chunk-Z4JULPCS.js} +1 -1
  112. package/dist/{chunk-UNPDFIPQ.js → chunk-Z5QJONDX.js} +2 -2
  113. package/dist/{chunk-PFHU3CW4.js → chunk-ZZ5QZ24I.js} +1 -1
  114. package/dist/{codex-app-cards-switch-XP2H2CZO.js → codex-app-cards-switch-QEMMPNK3.js} +11 -11
  115. package/dist/{codex-desktop-launch-QRFE2GHZ.js → codex-desktop-launch-LNIZPXSJ.js} +1 -1
  116. package/dist/{codex-session-launch-EUUWALWW.js → codex-session-launch-IZ4IQDS7.js} +18 -18
  117. package/dist/{config-WBDMUTD4.js → config-3J5OOY3O.js} +3 -3
  118. package/dist/{global-upgrade-ZXFLRGGV.js → global-upgrade-LQM7IFAP.js} +10 -10
  119. package/dist/{identity-NUQ75BP7.js → identity-IQ3JVAIM.js} +4 -4
  120. package/dist/{live-capabilities-SIQ25SSK.js → live-capabilities-3AWVBJSK.js} +5 -5
  121. package/dist/{paths-BGO6PZFZ.js → paths-TAAVCO2D.js} +3 -3
  122. package/dist/{reapply-AD5ATZOO.js → reapply-37FCVQCJ.js} +28 -28
  123. package/dist/src/index.js +29 -29
  124. package/dist/{upgrade-lock-B5HTMEWT.js → upgrade-lock-GWB57A36.js} +4 -4
  125. package/dist/{usage-YHFALPKW.js → usage-QJYVQ3GS.js} +3 -3
  126. package/package.json +1 -1
@@ -2062,7 +2062,7 @@ var operandTextsOf = (command) => command.args.flatMap((arg) => {
2062
2062
  });
2063
2063
  var positionalTextsOf = (command) => command.args.filter((arg) => parameterOf(arg) === null).map((arg) => arg.text);
2064
2064
  var firstOperandOf = (command, isSwitch) => command.args.find((arg) => !isSwitch.test(arg.text))?.text;
2065
- var namesASecretPath = (text3, judge) => judge.namesASecretFile(fileNameOf(text3.trim()));
2065
+ var namesAPathThatNeedsAPerson = (text3, judge) => judge.namesAFileThatNeedsAPerson(text3.trim());
2066
2066
  var isAssignmentTarget = (word) => POWERSHELL_ASSIGNMENT_TARGET.test(word.text) || word.text === POWERSHELL_ARRAY_SEPARATOR;
2067
2067
  var wordsAfterAnAssignment = (words) => {
2068
2068
  const operator = words.findIndex((word) => isPowerShellAssignmentOperator(word));
@@ -2121,10 +2121,10 @@ var copyDestinationOf = (command) => {
2121
2121
  };
2122
2122
  var writesASecretFileFromPowerShell = (command, judge) => {
2123
2123
  if (command.cmdlet !== COPY_ITEM) {
2124
- return POWERSHELL_FILE_WRITERS.has(command.cmdlet) && operandTextsOf(command).some((text3) => namesASecretPath(text3, judge));
2124
+ return POWERSHELL_FILE_WRITERS.has(command.cmdlet) && operandTextsOf(command).some((text3) => namesAPathThatNeedsAPerson(text3, judge));
2125
2125
  }
2126
2126
  const destination = copyDestinationOf(command);
2127
- return destination !== void 0 && namesASecretPath(destination, judge);
2127
+ return destination !== void 0 && namesAPathThatNeedsAPerson(destination, judge);
2128
2128
  };
2129
2129
  var formatsADisk = (command) => DISK_ERASERS.has(command.cmdlet);
2130
2130
  var changesAWindowsService = (command) => {
@@ -2142,7 +2142,7 @@ var stageIsDestructive = (stage, judge) => {
2142
2142
  };
2143
2143
  var stageHasAVisibleConsequence = (stage, judge) => {
2144
2144
  const command = powerShellCommandOf(stage);
2145
- return stage.redirectTargets.some((target) => namesASecretPath(target.text, judge)) || command !== null && !onlyReads(command, judge) && powerShellCommandChangesSomething(command, judge);
2145
+ return stage.redirectTargets.some((target) => namesAPathThatNeedsAPerson(target.text, judge)) || command !== null && !onlyReads(command, judge) && powerShellCommandChangesSomething(command, judge);
2146
2146
  };
2147
2147
  var fetchesWithPowerShell = (stage, judge) => {
2148
2148
  const command = powerShellCommandOf(stage);
@@ -4538,7 +4538,7 @@ var sqlClientChangesData = (invocation) => {
4538
4538
  var PULL_REQUEST_CLIS = /* @__PURE__ */ new Set(["gh", "glab"]);
4539
4539
  var PULL_REQUEST_NOUNS = /* @__PURE__ */ new Set(["pr", "mr"]);
4540
4540
  var MERGE_SUBCOMMAND = "merge";
4541
- var PULL_REQUEST_MERGE_PATH = /(?:^|\/)pulls\/\d+\/merge$/;
4541
+ var PULL_REQUEST_MERGE_PATH = /(?:^|\/)pulls\/[^/\s]+\/merge(?:[?#]|$)/;
4542
4542
  var mergesAPullRequest = ({ program, args }) => {
4543
4543
  if (program === void 0 || !PULL_REQUEST_CLIS.has(basenameOf(program.text))) return false;
4544
4544
  const words = args.filter((arg) => !arg.text.startsWith("-"));
@@ -4594,13 +4594,127 @@ var DEPLOY_AND_SCHEMA_COMMAND_PATTERNS = [
4594
4594
  "Bash(gcloud run deploy:*)",
4595
4595
  "Bash(gcloud functions deploy:*)"
4596
4596
  ];
4597
+ var RELEASE_AND_CLUSTER_PROGRAMS = /* @__PURE__ */ new Set(["kubectl", "fastlane"]);
4598
+ var RELEASE_AND_CLUSTER_COMMAND_PATTERNS = [
4599
+ "Bash(kubectl patch:*)",
4600
+ "Bash(kubectl edit:*)",
4601
+ "Bash(kubectl set:*)",
4602
+ "Bash(kubectl create:*)",
4603
+ "Bash(kubectl drain:*)",
4604
+ "Bash(kubectl cordon:*)",
4605
+ "Bash(kubectl taint:*)",
4606
+ "Bash(fastlane deliver:*)",
4607
+ "Bash(fastlane supply:*)",
4608
+ "Bash(fastlane pilot upload:*)",
4609
+ "Bash(fastlane upload_to_app_store:*)",
4610
+ "Bash(fastlane upload_to_testflight:*)",
4611
+ "Bash(fastlane upload_to_play_store:*)"
4612
+ ];
4613
+ var SUBMITS_THE_BUILD_TO_A_STORE = /^--auto-submit(?:-with-profile)?(?:=|$)/;
4614
+ var EAS_PROGRAM = /^eas(?:-cli)?(?:@[\w.-]+)?$/;
4615
+ var EAS_COMMANDS_THAT_PUBLISH = /^(?:submit|deploy|update|update:(?:republish|rollback|roll-back-to-embedded)|channel:(?:edit|rollout))$/;
4616
+ var easCommandPublishes = (invocation) => {
4617
+ const words = wordsOfInvocation(invocation);
4618
+ return words.some((word, index) => EAS_PROGRAM.test(basenameOf(word.text)) && EAS_COMMANDS_THAT_PUBLISH.test(words[index + 1]?.text ?? ""));
4619
+ };
4620
+ var publishesAnAppBuild = (invocation) => invocation.args.some((arg) => SUBMITS_THE_BUILD_TO_A_STORE.test(arg.text)) || easCommandPublishes(invocation);
4621
+ var PUSHES_EVERY_TAG = /* @__PURE__ */ new Set(["--tags", "--follow-tags"]);
4622
+ var NAMES_A_TAG = /(?:^|:)\+?refs\/tags\/|^tag$|(?:^|:)v?\d+(?:\.\d+){1,3}$|(?:^|:)v?\d+\.\d+\.\d+[-+][\w.]+$|\/v\d+\.\d+\.\d+(?:\.\d+)?(?:[-+][\w.]+)?$|^v\$/;
4623
+ var TAG_HELD_IN_A_VARIABLE = /^v?\$\{?(?:\w+_)?(?:tag|version)(?:_name)?\}?$/i;
4624
+ var GIT_PUSH = "push";
4625
+ var pushesATag = ({ program, args }) => {
4626
+ if (program === void 0 || basenameOf(program.text) !== GIT) return false;
4627
+ const [subcommand, ...rest] = argsAfterHarmlessGlobalFlags(args);
4628
+ if (subcommand?.text !== GIT_PUSH) return false;
4629
+ const operands = rest.filter((arg) => !FLAG.test(arg.text));
4630
+ return rest.some((arg) => PUSHES_EVERY_TAG.has(arg.text)) || operands.slice(1).some((operand) => NAMES_A_TAG.test(operand.text) || TAG_HELD_IN_A_VARIABLE.test(operand.text));
4631
+ };
4597
4632
  var SECRET_FILE_NAME = /^(?:\.env(?:\.(?!example$|sample$|template$)[\w.-]+)?|(?:secrets?|credentials)\.(?:json|ya?ml|env|toml|ini)|credentials)$/i;
4633
+ var CI_WORKFLOW_FILE = /(?:^|\/)\.github\/workflows\/[^/]+$/i;
4634
+ var AGENT_SETTINGS_FILE = new RegExp(String.raw`(?:^|/)(?:${[
4635
+ String.raw`\.claude/settings(?:\.local)?\.json`,
4636
+ String.raw`\.claude\.json`,
4637
+ String.raw`\.codex/(?:config\.toml|hooks\.json)`,
4638
+ String.raw`\.cursor/hooks\.json`,
4639
+ String.raw`\.gemini/(?:settings\.json|config/hooks\.json)`,
4640
+ String.raw`\.qwen/settings\.json`,
4641
+ String.raw`\.factory/hooks\.json`,
4642
+ String.raw`\.hermes/config\.ya?ml`,
4643
+ String.raw`\.config/opencode/(?:opencode\.jsonc?|plugins?/.+)`,
4644
+ String.raw`\.pushary(?:/.+)?`,
4645
+ String.raw`\.git/hooks(?:/.+)?`,
4646
+ String.raw`\.ssh(?:/(?!known_hosts(?:\.old)?$).+)?`
4647
+ ].join("|")})$`, "i");
4648
+ var withoutDotSegments = (path) => {
4649
+ const kept = [];
4650
+ for (const segment of path.split("/")) {
4651
+ if (segment === "..") kept.pop();
4652
+ else if (segment !== "" && segment !== ".") kept.push(segment);
4653
+ }
4654
+ return `${path.startsWith("/") ? "/" : ""}${kept.join("/")}`;
4655
+ };
4656
+ var pathNeedsAPersonToChange = (path) => {
4657
+ const written = withoutDotSegments(normalizeSeparators(path));
4658
+ return SECRET_FILE_NAME.test(basenameOf(written)) || CI_WORKFLOW_FILE.test(written) || AGENT_SETTINGS_FILE.test(written);
4659
+ };
4598
4660
  var REDIRECTS_INTO_A_SECRET_FILE = />>?\s*(?:[^\s;|&<>]*\/)?\.env(?:\.[\w-]+)?(?=$|[\s;|&)])/;
4599
4661
  var COPIES_ONTO_ITS_LAST_OPERAND = /* @__PURE__ */ new Set(["cp", "install", "ln", "rsync", "scp"]);
4600
- var WRITES_EVERY_OPERAND = /* @__PURE__ */ new Set(["mv", "tee", "rm", "unlink", "shred", "truncate"]);
4601
- var EDITS_IN_PLACE = /* @__PURE__ */ new Set(["sed", "gsed", "perl"]);
4602
- var IN_PLACE_FLAG = /^(?:-[A-Za-z]*i|--in-place(?:=.*)?)$/;
4603
- var namesASecretFile = (word) => word !== void 0 && SECRET_FILE_NAME.test(basenameOf(word.text));
4662
+ var WRITES_EVERY_OPERAND = /* @__PURE__ */ new Set(["mv", "tee", "rm", "unlink", "shred", "truncate", "sponge"]);
4663
+ var EDITS_IN_PLACE = /* @__PURE__ */ new Set(["sed", "gsed", "perl", "yq"]);
4664
+ var IN_PLACE_LONG_FLAG = /^--in-?place(?:=|$)/;
4665
+ var IN_PLACE_LETTER = "i";
4666
+ var SED_FLAGS_THAT_TAKE_THE_REST = /* @__PURE__ */ new Set(["e", "f", "l"]);
4667
+ var SHORT_FLAGS_THAT_TAKE_THE_REST = /* @__PURE__ */ new Map([
4668
+ ["sed", SED_FLAGS_THAT_TAKE_THE_REST],
4669
+ ["gsed", SED_FLAGS_THAT_TAKE_THE_REST],
4670
+ ["perl", /* @__PURE__ */ new Set(["M", "m", "I", "e", "E", "x", "F", "C", "d", "D"])]
4671
+ ]);
4672
+ var flagEditsInPlace = (program, flag) => {
4673
+ if (IN_PLACE_LONG_FLAG.test(flag)) return true;
4674
+ if (!SHORT_OPTIONS.test(flag)) return false;
4675
+ const takesTheRest = SHORT_FLAGS_THAT_TAKE_THE_REST.get(program);
4676
+ for (const letter of flag.slice(1)) {
4677
+ if (letter === IN_PLACE_LETTER) return true;
4678
+ if (takesTheRest?.has(letter) === true) return false;
4679
+ }
4680
+ return false;
4681
+ };
4682
+ var MOVE = "mv";
4683
+ var TARGET_DIRECTORY_FLAG = "-t";
4684
+ var TARGET_DIRECTORY_ASSIGNMENT = /^--target-directory=(.+)$/;
4685
+ var BRACE_ALTERNATIVES = /\{([^{},]*(?:,[^{},]*)+)\}/;
4686
+ var MAX_SPELLINGS_OF_A_WORD = 64;
4687
+ var hasABraceGroup = (spelling) => BRACE_ALTERNATIVES.test(spelling);
4688
+ var spellingsOfAWord = (word) => {
4689
+ if (!word.plain) return [word];
4690
+ let spellings = [word.text];
4691
+ for (let index = spellings.findIndex(hasABraceGroup); index !== -1; index = spellings.findIndex(hasABraceGroup)) {
4692
+ const spelling = spellings[index] ?? "";
4693
+ const braces = BRACE_ALTERNATIVES.exec(spelling);
4694
+ if (braces === null) return null;
4695
+ const choices = (braces[1] ?? "").split(",");
4696
+ if (spellings.length - 1 + choices.length > MAX_SPELLINGS_OF_A_WORD) return null;
4697
+ spellings = [...spellings.slice(0, index), ...choices.map((choice) => spelling.replace(braces[0], choice)), ...spellings.slice(index + 1)];
4698
+ }
4699
+ return spellings.map((text3) => ({ ...word, text: text3 }));
4700
+ };
4701
+ var namesAFileThatNeedsAPerson = (word) => word !== void 0 && pathNeedsAPersonToChange(word.text);
4702
+ var RECURSIVE_COPY_FLAG = /^(?:-[A-Za-z]*[rRa][A-Za-z]*|--recursive|--archive)$/;
4703
+ var FOLDER_THAT_NEEDS_A_PERSON = new RegExp(String.raw`(?:^|/)(?:${[
4704
+ String.raw`\.github/workflows`,
4705
+ String.raw`\.claude`,
4706
+ String.raw`\.codex`,
4707
+ String.raw`\.cursor`,
4708
+ String.raw`\.gemini`,
4709
+ String.raw`\.qwen`,
4710
+ String.raw`\.factory`,
4711
+ String.raw`\.hermes`,
4712
+ String.raw`\.pushary`,
4713
+ String.raw`\.git/hooks`,
4714
+ String.raw`\.ssh`,
4715
+ String.raw`\.config/opencode(?:/plugins?)?`
4716
+ ].join("|")})$`, "i");
4717
+ var folderNeedsAPersonToChange = (path) => FOLDER_THAT_NEEDS_A_PERSON.test(withoutDotSegments(normalizeSeparators(path)));
4604
4718
  var NETWORK_SENDERS = /* @__PURE__ */ new Set([
4605
4719
  "curl",
4606
4720
  "wget",
@@ -4618,16 +4732,46 @@ var NETWORK_SENDERS = /* @__PURE__ */ new Set([
4618
4732
  "rclone"
4619
4733
  ]);
4620
4734
  var FILE_REFERENCE_PREFIX = /^(?:[\w.-]+=)?@|^[\w.-]+:/;
4621
- var namesASecretFileReference = (word) => SECRET_FILE_NAME.test(basenameOf(word.text.replace(FILE_REFERENCE_PREFIX, "")));
4622
- var sendsASecretFile = ({ program, args }) => program !== void 0 && NETWORK_SENDERS.has(basenameOf(program.text)) && args.some(namesASecretFileReference);
4623
- var writesASecretFile = ({ program, args }) => {
4735
+ var referencesAFileThatNeedsAPerson = (word) => pathNeedsAPersonToChange(word.text.replace(FILE_REFERENCE_PREFIX, ""));
4736
+ var sendsAFileThatNeedsAPerson = ({ program, args }) => program !== void 0 && NETWORK_SENDERS.has(basenameOf(program.text)) && args.some(referencesAFileThatNeedsAPerson);
4737
+ var targetDirectoryOf = (args) => {
4738
+ const flag = args.findIndex((arg) => arg.text === TARGET_DIRECTORY_FLAG);
4739
+ if (flag !== -1) return args[flag + 1]?.text;
4740
+ return args.map((arg) => TARGET_DIRECTORY_ASSIGNMENT.exec(arg.text)?.[1]).find((directory) => directory !== void 0);
4741
+ };
4742
+ var copiesOntoAFileThatNeedsAPerson = (args, operands) => {
4743
+ const targetDirectory = targetDirectoryOf(args);
4744
+ const destination = targetDirectory ?? operands.at(-1)?.text;
4745
+ if (destination === void 0) return false;
4746
+ const sources = targetDirectory === void 0 ? operands.slice(0, -1) : operands.filter((operand) => operand.text !== targetDirectory);
4747
+ const landingPaths = [destination, ...sources.map((source) => `${destination}/${basenameOf(source.text)}`)];
4748
+ return landingPaths.some(pathNeedsAPersonToChange) || args.some((arg) => RECURSIVE_COPY_FLAG.test(arg.text)) && landingPaths.some(folderNeedsAPersonToChange);
4749
+ };
4750
+ var GIT_HOOKS_PATH_SETTING = /^core\.hookspath(?:=|$)/i;
4751
+ var GIT_ONE_COMMAND_SETTING_FLAG = "-c";
4752
+ var GIT_CONFIG_SUBCOMMAND = "config";
4753
+ var GIT_CONFIG_UNSET_FLAGS = /* @__PURE__ */ new Set(["--unset", "--unset-all"]);
4754
+ var changesWhereGitLooksForHooks = ({ program, args }) => {
4755
+ if (program === void 0 || basenameOf(program.text) !== GIT) return false;
4756
+ const setting = args.findIndex((arg) => GIT_HOOKS_PATH_SETTING.test(arg.text));
4757
+ if (setting === -1) return false;
4758
+ if (args[setting - 1]?.text === GIT_ONE_COMMAND_SETTING_FLAG) return true;
4759
+ return args.some((arg) => arg.text === GIT_CONFIG_SUBCOMMAND) && (args[setting + 1] !== void 0 || args.some((arg) => GIT_CONFIG_UNSET_FLAGS.has(arg.text)));
4760
+ };
4761
+ var writesAFileThatNeedsAPerson = ({ program, args }) => {
4624
4762
  if (program === void 0) return false;
4625
- if (sendsASecretFile({ program, args })) return true;
4763
+ if (sendsAFileThatNeedsAPerson({ program, args })) return true;
4626
4764
  const name = basenameOf(program.text);
4627
- const operands = args.filter((arg) => !FLAG.test(arg.text));
4628
- if (COPIES_ONTO_ITS_LAST_OPERAND.has(name)) return namesASecretFile(operands.at(-1));
4629
- if (WRITES_EVERY_OPERAND.has(name)) return operands.some(namesASecretFile);
4630
- return EDITS_IN_PLACE.has(name) && args.some((arg) => IN_PLACE_FLAG.test(arg.text)) && operands.some(namesASecretFile);
4765
+ const editsInPlace = EDITS_IN_PLACE.has(name) && args.some((arg) => flagEditsInPlace(name, arg.text));
4766
+ if (!COPIES_ONTO_ITS_LAST_OPERAND.has(name) && !WRITES_EVERY_OPERAND.has(name) && !editsInPlace) return false;
4767
+ const spelled = args.filter((arg) => !FLAG.test(arg.text)).map(spellingsOfAWord);
4768
+ if (spelled.includes(null)) return true;
4769
+ const operands = spelled.flatMap((words) => words ?? []);
4770
+ if (COPIES_ONTO_ITS_LAST_OPERAND.has(name)) return copiesOntoAFileThatNeedsAPerson(args, operands);
4771
+ if (WRITES_EVERY_OPERAND.has(name)) {
4772
+ return operands.some((operand) => namesAFileThatNeedsAPerson(operand) || folderNeedsAPersonToChange(operand.text)) || name === MOVE && copiesOntoAFileThatNeedsAPerson(args, operands);
4773
+ }
4774
+ return operands.some(namesAFileThatNeedsAPerson);
4631
4775
  };
4632
4776
  var GITHUB_COMMANDS_THAT_ONLY_READ = /* @__PURE__ */ new Set([
4633
4777
  "search",
@@ -4733,7 +4877,7 @@ var AWS_OPTIONS_WITH_VALUES = /* @__PURE__ */ new Set([
4733
4877
  "--cli-binary-format"
4734
4878
  ]);
4735
4879
  var AWS_LOCAL_COMMANDS = /* @__PURE__ */ new Set(["configure", "help", "history", "sso", "login", "logout"]);
4736
- var AWS_READ_OPERATION = /^(?:describe|list|get|head|batch-get|lookup|search|filter|scan|query|select|wait|validate|estimate|simulate|tail|ls|presign|help)(?:-|$)/;
4880
+ var AWS_READ_OPERATION = /^(?:describe|list|get|head|batch-get|lookup|search|filter|scan|query|select|wait|validate|estimate|simulate|tail|ls|presign|help|login|update-kubeconfig|start-query|stop-query|assume-role)(?:-|$)/;
4737
4881
  var AWS_S3_SERVICE = "s3";
4738
4882
  var AWS_S3_COPIES = /* @__PURE__ */ new Set(["cp", "sync"]);
4739
4883
  var S3_URL_PREFIX = "s3://";
@@ -4803,11 +4947,16 @@ var githubCommandOnlyReads = (noun, verb) => {
4803
4947
  const readVerbs = GITHUB_READ_COMMANDS.get(noun.text);
4804
4948
  return readVerbs !== void 0 && (verb === void 0 || readVerbs.has(verb.text));
4805
4949
  };
4806
- var githubCliWrites = (args) => {
4950
+ var githubCommandWordsOf = (args) => {
4807
4951
  const meaningful = argsAfterHarmlessGlobalFlags(args);
4808
4952
  const [noun, verb] = positionalWordsOf(meaningful, GITHUB_OPTIONS_WITH_VALUES);
4809
- if (noun?.text === GITHUB_API_SUBCOMMAND) return githubApiRequestWrites(meaningful.slice(meaningful.indexOf(noun) + 1));
4810
- return !githubCommandOnlyReads(noun, verb);
4953
+ return { meaningful, noun, verb };
4954
+ };
4955
+ var githubApiArgsOf = ({ meaningful, noun }) => noun === void 0 ? [] : meaningful.slice(meaningful.indexOf(noun) + 1);
4956
+ var githubCliWrites = (args) => {
4957
+ const words = githubCommandWordsOf(args);
4958
+ if (words.noun?.text === GITHUB_API_SUBCOMMAND) return githubApiRequestWrites(githubApiArgsOf(words));
4959
+ return !githubCommandOnlyReads(words.noun, words.verb);
4811
4960
  };
4812
4961
  var stripeCliWrites = (args) => {
4813
4962
  const [command, operation] = positionalWordsOf(args, STRIPE_OPTIONS_WITH_VALUES);
@@ -4826,8 +4975,9 @@ var twilioCliWrites = (args) => {
4826
4975
  if (topic === void 0 || !topic.text.includes(TWILIO_TOPIC_SEPARATOR)) return false;
4827
4976
  return !TWILIO_READ_ACTIONS.has(topic.text.split(TWILIO_TOPIC_SEPARATOR).at(-1) ?? "");
4828
4977
  };
4978
+ var GITHUB_CLI = "gh";
4829
4979
  var REMOTE_API_CLI_WRITES = /* @__PURE__ */ new Map([
4830
- ["gh", githubCliWrites],
4980
+ [GITHUB_CLI, githubCliWrites],
4831
4981
  ["stripe", stripeCliWrites],
4832
4982
  ["aws", awsCliWrites],
4833
4983
  ["twilio", twilioCliWrites]
@@ -4836,6 +4986,191 @@ var invocationWritesThroughARemoteApi = ({ program, args }) => {
4836
4986
  const writes = program === void 0 ? void 0 : REMOTE_API_CLI_WRITES.get(basenameOf(program.text));
4837
4987
  return writes !== void 0 && writes(args);
4838
4988
  };
4989
+ var GITHUB_REPO_NOUN = "repo";
4990
+ var GITHUB_EDIT_VERB = "edit";
4991
+ var GITHUB_CREATE_VERB = "create";
4992
+ var GITHUB_PUBLIC_OPTION = /^--public(?:=(?!false$).*)?$/;
4993
+ var GITHUB_VISIBILITY_OPTION = /^--visibility(?:=|$)/;
4994
+ var GITHUB_COMMANDS_WITH_A_CONSEQUENCE = /* @__PURE__ */ new Map([
4995
+ ["secret", /* @__PURE__ */ new Set(["set"])],
4996
+ ["variable", /* @__PURE__ */ new Set(["set"])],
4997
+ [GITHUB_REPO_NOUN, /* @__PURE__ */ new Set(["archive", "rename"])],
4998
+ ["ssh-key", /* @__PURE__ */ new Set(["add"])],
4999
+ ["gpg-key", /* @__PURE__ */ new Set(["add"])]
5000
+ ]);
5001
+ var HTTP_METHODS_THAT_CHANGE_A_RESOURCE = /* @__PURE__ */ new Set(["DELETE", "PUT", "PATCH"]);
5002
+ var GITHUB_API_ENDPOINTS_FOR_EVERYDAY_WORK = /(?:^|\/)(?:issues|pulls|comments|notifications|labels|assignees|reviews|reactions|requested_reviewers)(?:\/|$)/;
5003
+ var GITHUB_API_ENDPOINTS_THAT_PUBLISH = /(?:^|\/)(?:merges|releases|dispatches|transfer|keys|deployments|hooks|git\/refs)\/?$/;
5004
+ var GRAPHQL_QUERY_FIELD = /^query=/;
5005
+ var GRAPHQL_FIELD_WORDS_THAT_DELETE_OR_PUBLISH = /delete|archive|transfer|merge|branchprotectionrule/;
5006
+ var GRAPHQL_FIELDS_THAT_PUBLISH = /* @__PURE__ */ new Set([
5007
+ "updaterepository",
5008
+ "createref",
5009
+ "updateref",
5010
+ "updaterefs",
5011
+ "createcommitonbranch"
5012
+ ]);
5013
+ var WORDS_AND_THE_TEXT_BETWEEN = /(\W+)/;
5014
+ var OPENS_A_CALL = /^\s*\(/;
5015
+ var GRAPHQL_STRING = /"(?:[^"\\]|\\.)*"/g;
5016
+ var graphqlFieldsCalledIn = (query) => {
5017
+ const parts = query.replace(GRAPHQL_STRING, '""').split(WORDS_AND_THE_TEXT_BETWEEN);
5018
+ return parts.filter((part, index) => index % 2 === 0 && part !== "" && OPENS_A_CALL.test(parts[index + 1] ?? "")).map((field) => field.toLowerCase());
5019
+ };
5020
+ var graphqlFieldDeletesOrPublishes = (field) => GRAPHQL_FIELDS_THAT_PUBLISH.has(field) || GRAPHQL_FIELD_WORDS_THAT_DELETE_OR_PUBLISH.test(field);
5021
+ var SINGLE_SHORT_OPTION = /^-[A-Za-z]$/;
5022
+ var takesTheNextGithubApiWord = (text3) => GITHUB_API_LONG_OPTIONS_WITH_A_VALUE.has(text3) || SINGLE_SHORT_OPTION.test(text3) && GITHUB_API_SHORT_OPTIONS_WITH_A_VALUE.has(text3.charAt(1));
5023
+ var githubApiEndpointOf = (args) => (args.find((arg, index) => !FLAG.test(arg.text) && !takesTheNextGithubApiWord(args[index - 1]?.text ?? ""))?.text ?? "").replace(URL_QUERY_OR_FRAGMENT2, "");
5024
+ var graphqlQueriesOf = (options) => options.flatMap(({ name, value }) => value !== void 0 && GITHUB_API_BODY_OPTIONS.has(name) && GRAPHQL_QUERY_FIELD.test(value.text) ? [value] : []);
5025
+ var graphqlQueryHasAConsequence = (query) => query.expands || GITHUB_API_FILE_FIELD.test(query.text) || GRAPHQL_MUTATION.test(query.text) && graphqlFieldsCalledIn(query.text).some(graphqlFieldDeletesOrPublishes);
5026
+ var graphqlRequestHasAConsequence = (options) => {
5027
+ const queries = graphqlQueriesOf(options);
5028
+ return queries.length === 0 || queries.some(graphqlQueryHasAConsequence);
5029
+ };
5030
+ var HTTP_DELETE = "DELETE";
5031
+ var githubApiMethodHasAConsequence = (method, endpoint) => {
5032
+ if (method.expands) return true;
5033
+ const name = method.text.toUpperCase();
5034
+ return name === HTTP_DELETE || HTTP_METHODS_THAT_CHANGE_A_RESOURCE.has(name) && !GITHUB_API_ENDPOINTS_FOR_EVERYDAY_WORK.test(endpoint);
5035
+ };
5036
+ var githubApiRequestHasAConsequence = (args) => {
5037
+ const options = githubApiOptionsOf(args);
5038
+ if (options === null) return true;
5039
+ const endpoint = githubApiEndpointOf(args);
5040
+ if (endpoint === GITHUB_GRAPHQL_ENDPOINT) return graphqlRequestHasAConsequence(options);
5041
+ if (GITHUB_API_ENDPOINTS_THAT_PUBLISH.test(endpoint)) return githubApiRequestWrites(args);
5042
+ const method = options.filter((option) => HTTP_METHOD_OPTIONS.has(option.name)).at(-1)?.value;
5043
+ return method !== void 0 && githubApiMethodHasAConsequence(method, endpoint);
5044
+ };
5045
+ var githubCommandHasAConsequence = ({ program, args }) => {
5046
+ if (program === void 0 || basenameOf(program.text) !== GITHUB_CLI) return false;
5047
+ const words = githubCommandWordsOf(args);
5048
+ const { noun, verb } = words;
5049
+ if (noun === void 0) return false;
5050
+ if (noun.text === GITHUB_API_SUBCOMMAND) return githubApiRequestHasAConsequence(githubApiArgsOf(words));
5051
+ if (noun.text === GITHUB_REPO_NOUN && verb?.text === GITHUB_EDIT_VERB) return words.meaningful.some((arg) => GITHUB_VISIBILITY_OPTION.test(arg.text));
5052
+ if (noun.text === GITHUB_REPO_NOUN && verb?.text === GITHUB_CREATE_VERB) return words.meaningful.some((arg) => GITHUB_PUBLIC_OPTION.test(arg.text));
5053
+ return verb !== void 0 && (GITHUB_COMMANDS_WITH_A_CONSEQUENCE.get(noun.text)?.has(verb.text) ?? false);
5054
+ };
5055
+ var STRIPE_CLI = "stripe";
5056
+ var STRIPE_LIVE_MODE = /^--live$|[sr]k_live_/;
5057
+ var STRIPE_API_KEY_OPTION = "--api-key";
5058
+ var STRIPE_API_KEY_HELD_IN_A_VARIABLE = /^(?:--api-key|STRIPE_API_KEY)=.*\$/;
5059
+ var stripeMayUseLiveData = (words) => words.some((word, index) => STRIPE_LIVE_MODE.test(word.text) || STRIPE_API_KEY_HELD_IN_A_VARIABLE.test(word.text) || word.expands && words[index - 1]?.text === STRIPE_API_KEY_OPTION);
5060
+ var writesThroughAServiceApi = (invocation) => {
5061
+ const innermost = innermostInvocation(invocation);
5062
+ const name = basenameOf(innermost.program?.text ?? "");
5063
+ if (name === GITHUB_CLI) return false;
5064
+ if (name === STRIPE_CLI && !stripeMayUseLiveData(wordsOfInvocation(invocation))) return false;
5065
+ return invocationWritesThroughARemoteApi(innermost);
5066
+ };
5067
+ var NO_SHORT_OPTIONS = /* @__PURE__ */ new Set();
5068
+ var HTTP_CLIENT_SYNTAX = /* @__PURE__ */ new Map([
5069
+ ["curl", {
5070
+ bodyOption: /^--(?:data(?:-[a-z]+)?|json|form(?:-string)?|upload-file)(?:=|$)/,
5071
+ methodOption: /^--request(?:=|$)/,
5072
+ getOption: /^--get$/,
5073
+ shortOptionsWithAValue: /* @__PURE__ */ new Set([..."AbcCdDeEFHKmoPQrtTuUwxXyYz"]),
5074
+ shortBodyOptions: /* @__PURE__ */ new Set(["d", "F", "T"]),
5075
+ shortMethodOption: "X",
5076
+ shortGetOption: "G"
5077
+ }],
5078
+ ["wget", {
5079
+ bodyOption: /^--(?:post-(?:data|file)|body-(?:data|file))(?:=|$)/,
5080
+ methodOption: /^--method(?:=|$)/,
5081
+ getOption: null,
5082
+ shortOptionsWithAValue: NO_SHORT_OPTIONS,
5083
+ shortBodyOptions: NO_SHORT_OPTIONS,
5084
+ shortMethodOption: null,
5085
+ shortGetOption: null
5086
+ }]
5087
+ ]);
5088
+ var SHORT_OPTIONS = /^-[^-]/;
5089
+ var optionValueAt = (args, index, attached) => {
5090
+ const arg = args[index];
5091
+ return attached === "" || arg === void 0 ? args[index + 1] : { ...arg, text: attached };
5092
+ };
5093
+ var readHttpRequest = (args, syntax) => {
5094
+ let sendsABody = false;
5095
+ let forcesAGet = false;
5096
+ const methods = [];
5097
+ args.forEach((arg, index) => {
5098
+ if (LONG_OPTION.test(arg.text)) {
5099
+ sendsABody ||= syntax.bodyOption.test(arg.text);
5100
+ forcesAGet ||= syntax.getOption?.test(arg.text) ?? false;
5101
+ const separator = arg.text.indexOf(FLAG_VALUE_SEPARATOR);
5102
+ const method = syntax.methodOption.test(arg.text) ? optionValueAt(args, index, separator === -1 ? "" : arg.text.slice(separator + 1)) : void 0;
5103
+ if (method !== void 0) methods.push(method);
5104
+ return;
5105
+ }
5106
+ if (!SHORT_OPTIONS.test(arg.text)) return;
5107
+ for (let position = 1; position < arg.text.length; position += 1) {
5108
+ const letter = arg.text.charAt(position);
5109
+ forcesAGet ||= letter === syntax.shortGetOption;
5110
+ if (!syntax.shortOptionsWithAValue.has(letter)) continue;
5111
+ sendsABody ||= syntax.shortBodyOptions.has(letter);
5112
+ const method = letter === syntax.shortMethodOption ? optionValueAt(args, index, arg.text.slice(position + 1)) : void 0;
5113
+ if (method !== void 0) methods.push(method);
5114
+ return;
5115
+ }
5116
+ });
5117
+ return { sendsABody, forcesAGet, methods };
5118
+ };
5119
+ var requestSendsData = ({ sendsABody, forcesAGet, methods }) => methods.length === 0 ? sendsABody && !forcesAGet : methods.some((method) => method.expands || !HTTP_READ_METHODS.has(method.text.toUpperCase()));
5120
+ var THIS_MACHINE_ADDRESS = /^(?:(?:https?:\/\/)?(?:(?:[\w-]+\.)*localhost|127(?:\.\d{1,3}){3}|0\.0\.0\.0|\[::1\]|host\.docker\.internal)|https?:\/\/(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2}))(?::(?:\d+|\$\w+|\$\{[^}]*\}))?(?:[/?#]|$)/i;
5121
+ var ROUTES_THE_REQUEST_ELSEWHERE = /^(?:--(?:resolve|connect-to|proxy[\w.]*|preproxy|socks[\w-]*|config)(?:=|$)|-[A-Za-z]*[xK])/;
5122
+ var URL_WITH_A_SCHEME = /^[a-z][a-z\d+.-]*:\/\//i;
5123
+ var requestStaysOnThisMachine = (args) => {
5124
+ if (args.some((arg) => ROUTES_THE_REQUEST_ELSEWHERE.test(arg.text))) return false;
5125
+ const addresses = args.filter((arg) => !FLAG.test(arg.text) && (URL_WITH_A_SCHEME.test(arg.text) || THIS_MACHINE_ADDRESS.test(arg.text)));
5126
+ return addresses.length > 0 && addresses.every((arg) => THIS_MACHINE_ADDRESS.test(arg.text));
5127
+ };
5128
+ var sendsDataOffThisMachine = ({ program, args }) => {
5129
+ const syntax = program === void 0 ? void 0 : HTTP_CLIENT_SYNTAX.get(basenameOf(program.text));
5130
+ return syntax !== void 0 && requestSendsData(readHttpRequest(args, syntax)) && !requestStaysOnThisMachine(args);
5131
+ };
5132
+ var KUBECTL = "kubectl";
5133
+ var KUBECTL_EXEC = "exec";
5134
+ var kubectlExecRunsMoreThanAReadOnlyCommand = ({ program, args }) => {
5135
+ if (program === void 0 || basenameOf(program.text) !== KUBECTL) return false;
5136
+ const endOfOptions = args.findIndex((arg) => arg.text === END_OF_OPTIONS3);
5137
+ const kubectlWords = endOfOptions === -1 ? args : args.slice(0, endOfOptions);
5138
+ if (!kubectlWords.some((arg) => arg.text === KUBECTL_EXEC)) return false;
5139
+ const command = endOfOptions === -1 ? [] : args.slice(endOfOptions + 1);
5140
+ return command.length === 0 || command.some((arg) => arg.expands) || !isSafeReadOnlyCommand(posixCommandOf(command.map((arg) => arg.text)));
5141
+ };
5142
+ var SSH = "ssh";
5143
+ var SSH_OPTION_LETTERS_WITH_A_VALUE = /* @__PURE__ */ new Set([..."BbcDEeFIiJLlmOoPpQRSWw"]);
5144
+ var SSH_SHORT_OPTION_CLUSTER = /^-[A-Za-z]+$/;
5145
+ var SSH_OPTION_THAT_RUNS_A_COMMAND = /(?:proxy|local|remote|knownhosts)command/i;
5146
+ var SSH_CONFIG_OPTION = "-o";
5147
+ var sshOptionTakesTheNextWord = (text3) => {
5148
+ if (!SSH_SHORT_OPTION_CLUSTER.test(text3)) return false;
5149
+ const letters = text3.slice(1);
5150
+ const firstValueOption = [...letters].findIndex((letter) => SSH_OPTION_LETTERS_WITH_A_VALUE.has(letter));
5151
+ return firstValueOption === letters.length - 1;
5152
+ };
5153
+ var sshOptionRunsACommand = (args) => args.some((arg, index) => (args[index - 1]?.text === SSH_CONFIG_OPTION || arg.text.startsWith(SSH_CONFIG_OPTION)) && SSH_OPTION_THAT_RUNS_A_COMMAND.test(arg.text));
5154
+ var sshRemoteCommandOf = (args) => {
5155
+ let index = 0;
5156
+ while (index < args.length) {
5157
+ const text3 = args[index]?.text ?? "";
5158
+ if (text3 === END_OF_OPTIONS3) {
5159
+ index += 1;
5160
+ break;
5161
+ }
5162
+ if (!FLAG.test(text3)) break;
5163
+ index += sshOptionTakesTheNextWord(text3) ? 2 : 1;
5164
+ }
5165
+ return args.slice(index + 1);
5166
+ };
5167
+ var sshRunsMoreThanAReadOnlyCommand = ({ program, args }) => {
5168
+ if (program === void 0 || basenameOf(program.text) !== SSH) return false;
5169
+ if (sshOptionRunsACommand(args)) return true;
5170
+ const command = sshRemoteCommandOf(args);
5171
+ return command.length > 0 && (command.some((arg) => arg.expands) || !isSafeReadOnlyCommand(command.map((arg) => arg.text).join(" ")));
5172
+ };
5173
+ var changesSomethingHostedElsewhere = (invocation) => githubCommandHasAConsequence(invocation) || writesThroughAServiceApi(invocation) || sendsDataOffThisMachine(invocation) || kubectlExecRunsMoreThanAReadOnlyCommand(invocation) || sshRunsMoreThanAReadOnlyCommand(invocation);
4839
5174
  var segmentWritesThroughARemoteApi = (segment, depth) => {
4840
5175
  const innermost = innermostInvocation(segment.invocation);
4841
5176
  return invocationWritesThroughARemoteApi(innermost) || commandsNestedIn(innermost, inlineScriptsOf(innermost, segment.heredocs)).some((nested) => commandWritesThroughARemoteApiAt(nested, depth + 1));
@@ -4853,6 +5188,7 @@ var commandNeedsAPerson = (toolName, command) => {
4853
5188
  const segments = readableCommandSegments(command);
4854
5189
  return segments === null || classifyDecisionRisk(readingTool, command) !== "standard" || segments.some((segment) => segmentNeedsAPerson(readingTool, segment)) || pipesIntoASqlClient(command) || shellDialectOf(toolName) === "powershell" && powerShellCommandHasAVisibleConsequence(command, posixShellJudgeFor(toolName));
4855
5190
  };
5191
+ var sessionGrantMustAskAbout = (toolName, command) => commandNeedsAPerson(toolName, command) || SHELL_DIALECT_BY_TOOL.has(toolName) && commandHasAVisibleConsequence(toolName, command);
4856
5192
  var VISIBLE_DATA_LOSS_PATTERNS = [
4857
5193
  "Bash(git branch -D:*)",
4858
5194
  "Bash(git branch --delete:*)",
@@ -5134,15 +5470,17 @@ var honoursPreviewFlags = ({ program, args }) => {
5134
5470
  };
5135
5471
  var onlyPreviews = (invocation) => invocation.program !== void 0 && honoursPreviewFlags(invocation) && invocation.args.some((arg) => !arg.expands && PREVIEW_FLAG.test(arg.text));
5136
5472
  var changesWhatIsDeployed = (toolName, invocation, normalized) => !onlyPreviews(invocation) && (changesProduction(invocation) || mergesAPullRequest(invocation) || isCoveredByPolicy(DEPLOY_AND_SCHEMA_COMMAND_PATTERNS, toolName, normalized));
5473
+ var releasesOrChangesACluster = (toolName, invocation, normalized) => !onlyPreviews(invocation) && (pushesATag(invocation) || publishesAnAppBuild(invocation) || RELEASE_AND_CLUSTER_PROGRAMS.has(basenameOf(invocation.program?.text ?? "")) && isCoveredByPolicy(RELEASE_AND_CLUSTER_COMMAND_PATTERNS, toolName, normalized));
5137
5474
  var invocationHasAVisibleConsequence = (toolName, invocation, depth) => {
5138
5475
  if (depth > MAX_CONSEQUENCE_DEPTH) return true;
5139
5476
  if (invocation.program === void 0) return false;
5140
5477
  const normalized = quotedInvocation(invocation);
5141
5478
  const innermost = innermostInvocation(invocation);
5142
5479
  const script = shellScriptOf(invocation);
5143
- return classifyDecisionRisk(toolName, normalized) === "destructive" || changesWhatIsDeployed(toolName, invocation, normalized) || writesASecretFile(invocation) || losesDataVisibly(toolName, invocation, normalized, depth) || subcommandDestroysVisibly(invocation) || sqlClientChangesData(invocation) || innermost !== invocation && invocationHasAVisibleConsequence(toolName, innermost, depth) || script === null && passesAnInlineScript(invocation) || script !== null && commandHasAVisibleConsequenceAt(toolName, script, depth + 1) || readingsPastGlobalFlags(invocation).some((reading) => invocationHasAVisibleConsequence(toolName, reading, depth + 1));
5480
+ return classifyDecisionRisk(toolName, normalized) === "destructive" || changesWhatIsDeployed(toolName, invocation, normalized) || releasesOrChangesACluster(toolName, invocation, normalized) || writesAFileThatNeedsAPerson(invocation) || changesWhereGitLooksForHooks(invocation) || changesSomethingHostedElsewhere(invocation) || losesDataVisibly(toolName, invocation, normalized, depth) || subcommandDestroysVisibly(invocation) || sqlClientChangesData(invocation) || innermost !== invocation && invocationHasAVisibleConsequence(toolName, innermost, depth) || script === null && passesAnInlineScript(invocation) || script !== null && commandHasAVisibleConsequenceAt(toolName, script, depth + 1) || readingsPastGlobalFlags(invocation).some((reading) => invocationHasAVisibleConsequence(toolName, reading, depth + 1));
5144
5481
  };
5145
5482
  var RUNS_FETCHED_CODE = /(?:\$\(|`|<\()\s*(?:curl|wget)\b/;
5483
+ var RUNS_DECODED_CODE = /(?:^|[;&|(\s])(?:bash|sh|zsh|source|\.|python3?|node|ruby|perl)\s+(?:-[A-Za-z]+\s+)*["']?(?:<\(|\$\()[^)]*\b(?:base64|xxd|openssl)\b/;
5146
5484
  var FETCHING_PROGRAMS = /* @__PURE__ */ new Set(["curl", "wget"]);
5147
5485
  var READS_ITS_SCRIPT_FROM_STANDARD_INPUT = /^-[A-Za-z]*s[A-Za-z]*$/;
5148
5486
  var interpreterNameOf = (program) => basenameOf(program.text).replace(/\d+(?:\.\d+)*$/, "");
@@ -5162,7 +5500,79 @@ var runsADownloadedScript = (invocations) => {
5162
5500
  };
5163
5501
  var pipedSegmentsOf = (command) => (shellSegmentsReadingAPipe(commandForReading(command)) ?? []).map((segment) => innermostInvocation(invocationOf(withoutShellGrouping(segment))));
5164
5502
  var pipesIntoASqlClient = (command) => pipedSegmentsOf(command).some((invocation) => sqlClientSyntaxOf(invocation) !== void 0);
5165
- var pipesFetchedCodeIntoAnInterpreter = (command, invocations) => invocations.some(fetches) && pipedSegmentsOf(command).some(runsWhatItReads);
5503
+ var DECODING_FLAGS_BY_PROGRAM = /* @__PURE__ */ new Map([
5504
+ ["base64", /^(?:-[A-Za-z]*[dD]|--decode)$/],
5505
+ ["xxd", /^-[A-Za-z]*r/],
5506
+ ["openssl", /^-d$/]
5507
+ ]);
5508
+ var decodes = (invocation) => {
5509
+ const { program, args } = innermostInvocation(invocation);
5510
+ const decodingFlags = program === void 0 ? void 0 : DECODING_FLAGS_BY_PROGRAM.get(basenameOf(program.text));
5511
+ return decodingFlags !== void 0 && args.some((arg) => decodingFlags.test(arg.text));
5512
+ };
5513
+ var producesCodeTheCommandDoesNotShow = (invocation) => fetches(invocation) || decodes(invocation);
5514
+ var pipesHiddenCodeIntoAnInterpreter = (command, invocations) => invocations.some(producesCodeTheCommandDoesNotShow) && pipedSegmentsOf(command).some(runsWhatItReads);
5515
+ var CODE_THAT_DELETES_RECURSIVELY = /\b(?:rmtree|rimraf|rm_rf|rm_r|removedirs)\b|\b(?:rm|rmSync|remove|rmdir|rmdirSync)\s*\([^)]*recursive/;
5516
+ var CODE_THAT_DECODES = /\b(?:b64decode|atob|base64)\b/;
5517
+ var CODE_THAT_RUNS_CODE_IT_BUILDS = /(?<![.\w])(?:eval|exec)\s*\(/;
5518
+ var CODE_THAT_RUNS_COMMANDS = /\b(?:execSync|execFileSync|spawnSync|execFile|spawn|exec|child_process|subprocess|popen|Popen|system)\b/;
5519
+ var CODE_THAT_WRITES_A_FILE = /\b(?:writeFile|appendFile|copyFile|cpSync|renameSync|symlinkSync)\w*\s*\(|(?<![\w.])(?:cp|rename|symlink)\s*\(|\bfs(?:\.promises)?\.(?:cp|rename|symlink)\s*\(|\bshutil\.(?:copy\w*|move)\s*\(|\bos\.(?:rename|replace|symlink)\s*\(|\.write_(?:text|bytes)\s*\(|\bopen\s*\([^)]*,\s*['"`][wax]/;
5520
+ var CODE_STRING_LITERAL = /'([^'\\\n]*)'|"([^"\\\n]*)"|`([^`\\\n]*)`/g;
5521
+ var CODE_PATH_DIVISION = /'\s*\/\s*'/g;
5522
+ var DOUBLE_QUOTE2 = /"/g;
5523
+ var PATH_WITH_A_DOT = /\./;
5524
+ var LONGEST_PATH_SPELLED_IN_PARTS = 4;
5525
+ var SHELL_COMMAND_IN_A_STRING = /\S\s+\S/;
5526
+ var stringLiteralsOf = (code) => [...code.matchAll(CODE_STRING_LITERAL)].map((literal) => literal[1] ?? literal[2] ?? literal[3] ?? "");
5527
+ var pathsSpelledIn = (code) => {
5528
+ const literals = stringLiteralsOf(code.replace(DOUBLE_QUOTE2, "'").replace(CODE_PATH_DIVISION, "/"));
5529
+ return literals.flatMap((_, start) => Array.from({ length: Math.min(LONGEST_PATH_SPELLED_IN_PARTS, literals.length - start) }, (_2, extra) => literals.slice(start, start + extra + 1).join("/"))).filter((spelled) => PATH_WITH_A_DOT.test(spelled));
5530
+ };
5531
+ var commandsSpelledIn = (code) => [
5532
+ ...stringLiteralsOf(code).filter((literal) => SHELL_COMMAND_IN_A_STRING.test(literal)),
5533
+ ...code.split("\n").map((line) => stringLiteralsOf(line).join(" ")).filter((line) => SHELL_COMMAND_IN_A_STRING.test(line))
5534
+ ];
5535
+ var INTERPRETERS_THAT_TAKE_INLINE_CODE = /* @__PURE__ */ new Set([...INLINE_CODE_INTERPRETERS, "bun", "deno", "tsx", "ts-node"]);
5536
+ var RUNNERS_OF_AN_INTERPRETER = /* @__PURE__ */ new Set([
5537
+ "uv",
5538
+ "poetry",
5539
+ "pnpm",
5540
+ "npm",
5541
+ "yarn",
5542
+ "bundle",
5543
+ "pipenv",
5544
+ "hatch",
5545
+ "pdm",
5546
+ "rye",
5547
+ "conda",
5548
+ "mamba"
5549
+ ]);
5550
+ var isAnInterpreter = (word) => INTERPRETERS_THAT_TAKE_INLINE_CODE.has(interpreterNameOf(word));
5551
+ var interpreterInvocationOf = (invocation) => {
5552
+ const innermost = innermostInvocation(invocation);
5553
+ const { program, args } = innermost;
5554
+ if (program === void 0) return [];
5555
+ if (isAnInterpreter(program)) return [innermost];
5556
+ if (!RUNNERS_OF_AN_INTERPRETER.has(basenameOf(program.text))) return [];
5557
+ const start = args.findIndex(isAnInterpreter);
5558
+ const interpreter = args[start];
5559
+ return interpreter === void 0 ? [] : [{ program: interpreter, args: args.slice(start + 1) }];
5560
+ };
5561
+ var inlineCodeOf = ({ args }) => args.flatMap((arg, index) => {
5562
+ const namesInlineCode = (FLAG.test(arg.text) || arg.text === INLINE_CODE_SUBCOMMAND) && arg.text !== STANDARD_INPUT_ARGUMENT;
5563
+ if (!namesInlineCode || !passesInlineCode(arg.text)) return [];
5564
+ const separator = arg.text.indexOf(FLAG_VALUE_SEPARATOR);
5565
+ if (separator !== -1) return [arg.text.slice(separator + 1)];
5566
+ const code = args[index + 1];
5567
+ return code === void 0 ? [] : [code.text];
5568
+ });
5569
+ var interpreterCodeIn = (command, invocations) => {
5570
+ const interpreters = invocations.flatMap(interpreterInvocationOf);
5571
+ const heredocs = interpreters.some(runsWhatItReads) ? withHeredocBodiesSetAside(command).bodies : [];
5572
+ return [...interpreters.flatMap(inlineCodeOf), ...heredocs];
5573
+ };
5574
+ var codeNeedsAPerson = (code, depth) => CODE_THAT_DELETES_RECURSIVELY.test(code) || CODE_THAT_RUNS_CODE_IT_BUILDS.test(code) && CODE_THAT_DECODES.test(code) || CODE_THAT_WRITES_A_FILE.test(code) && pathsSpelledIn(code).some(pathNeedsAPersonToChange) || CODE_THAT_RUNS_COMMANDS.test(code) && commandsSpelledIn(code).some((command) => commandHasAVisibleConsequenceAt(SHELL_TOOL, command, depth + 1));
5575
+ var runsInterpreterCodeThatNeedsAPerson = (command, invocations, depth) => interpreterCodeIn(command, invocations).some((code) => codeNeedsAPerson(code, depth));
5166
5576
  var WORDS_THAT_OPEN_A_COMMAND = String.raw`if|then|elif|else|do|while|until|!`;
5167
5577
  var SHELL_GROUPING_PREFIX = new RegExp(String.raw`^\s*(?:(?:${WORDS_THAT_OPEN_A_COMMAND})\s+|[({]\s*)+`);
5168
5578
  var GROUP_OPENER_BY_CLOSER = /* @__PURE__ */ new Map([[")", "("], ["}", "{"]]);
@@ -5201,7 +5611,7 @@ var withHeredocBodiesSetAside = (command) => {
5201
5611
  };
5202
5612
  var REDIRECTION_TARGET = String.raw`(?:'[^']*'|"[^"]*"|[^\s;|&()<>]+)`;
5203
5613
  var INPUT_REDIRECTION = new RegExp(String.raw`\d*(?:<<<|<(?![<(&>]))\s*${REDIRECTION_TARGET}`, "g");
5204
- var OUTPUT_REDIRECTION = new RegExp(String.raw`(?:\d*|&)(?:>>?|>\||<>|>&|<&)\s*${REDIRECTION_TARGET}`, "g");
5614
+ var OUTPUT_REDIRECTION = new RegExp(String.raw`(?<!\d)(?:\d*|&)(?:>>?|>\||<>|>&|<&)\s*(${REDIRECTION_TARGET})`, "g");
5205
5615
  var REDIRECTED_INPUT_MARK = "\uE000";
5206
5616
  var ROUGH_SEPARATORS = ["&&", "||", "$(", "\n", ";", "|", "&", "`", "(", ")", "{", "}"];
5207
5617
  var SEPARATORS_INSIDE_DOUBLE_QUOTES = /* @__PURE__ */ new Set(["$(", "`", ")"]);
@@ -5243,6 +5653,10 @@ var invocationOfRoughPiece = (text3) => {
5243
5653
  const parsed = invocationOf(piece);
5244
5654
  return parsed.program !== void 0 ? parsed : invocationOf(piece.replace(QUOTE_CHARACTERS, ""));
5245
5655
  };
5656
+ var substitutionFedTo = (pieces, index) => {
5657
+ const next = pieces[index + 1];
5658
+ return next !== void 0 && SUBSTITUTION_OPENERS.has(next.before) ? next.text : null;
5659
+ };
5246
5660
  var roughSegmentsOf = (command) => {
5247
5661
  const { text: text3, bodies } = withHeredocBodiesSetAside(commandForReading(command));
5248
5662
  const pieces = roughPiecesOf(text3.replace(INPUT_REDIRECTION, ` ${REDIRECTED_INPUT_MARK} `).replace(OUTPUT_REDIRECTION, " "));
@@ -5251,34 +5665,88 @@ var roughSegmentsOf = (command) => {
5251
5665
  const heredocCount = occurrencesOf(piece.text, HEREDOC_MARK);
5252
5666
  const heredocs = bodies.slice(heredocsSeen, heredocsSeen + heredocCount);
5253
5667
  heredocsSeen += heredocCount;
5668
+ const substitution = substitutionFedTo(pieces, index);
5254
5669
  return {
5255
5670
  invocation: invocationOfRoughPiece(piece.text),
5256
5671
  readsInput: piece.text.includes(REDIRECTED_INPUT_MARK) || heredocCount > 0 || piece.before === PIPE3,
5257
5672
  readsAPipe: piece.before === PIPE3,
5258
- feedsASubstitution: SUBSTITUTION_OPENERS.has(pieces[index + 1]?.before ?? ""),
5673
+ feedsASubstitution: substitution !== null,
5674
+ substitution,
5675
+ evalRunsOneEnvironmentSetup: evalRunsOneEnvironmentSetup(pieces, index),
5259
5676
  heredocs
5260
5677
  };
5261
5678
  });
5262
5679
  };
5263
- var roughSegmentHasAVisibleConsequence = (toolName, segment, fetchesCode, depth) => {
5680
+ var ENVIRONMENT_SETUP_COMMANDS = [
5681
+ String.raw`fnm\s+env`,
5682
+ String.raw`brew\s+shellenv`,
5683
+ String.raw`(?:py|rb|nod|go|j)env\s+(?:virtualenv-)?init`,
5684
+ String.raw`direnv\s+(?:export|hook)`,
5685
+ "ssh-agent",
5686
+ String.raw`mise\s+(?:activate|env)`,
5687
+ String.raw`rtx\s+activate`,
5688
+ String.raw`zoxide\s+init`,
5689
+ String.raw`starship\s+init`,
5690
+ String.raw`conda\s+shell\.\w+\s+hook`,
5691
+ String.raw`minikube\s+docker-env`,
5692
+ String.raw`docker-machine\s+env`,
5693
+ String.raw`opam\s+env`,
5694
+ String.raw`aws\s+configure\s+export-credentials`,
5695
+ String.raw`poetry\s+env\s+activate`,
5696
+ "dircolors",
5697
+ String.raw`uv\s+generate-shell-completion`,
5698
+ String.raw`fzf\s+--(?:zsh|bash)`,
5699
+ String.raw`thefuck\s+--alias`,
5700
+ String.raw`luarocks\s+path`,
5701
+ String.raw`op\s+signin`,
5702
+ String.raw`(?:gh|kubectl|helm|npm|pnpm|rustup|deno|bun)\s+completion`,
5703
+ String.raw`(?:micro)?mamba\s+shell\s+hook`,
5704
+ "path_helper"
5705
+ ];
5706
+ var SETS_UP_THE_ENVIRONMENT = new RegExp(
5707
+ String.raw`^\s*(?:[^\s()$\x60"';|&]*/)?(?:${ENVIRONMENT_SETUP_COMMANDS.join("|")})(?:\s[^()$\x60"';|&]*)?$`
5708
+ );
5709
+ var EVAL_BEFORE_ITS_ARGUMENT = /^\s*eval\s+["']?$/;
5710
+ var EVAL_AFTER_ITS_ARGUMENT = /^["']?\s*$/;
5711
+ var SUBSTITUTION_CLOSERS = /* @__PURE__ */ new Set([")", "`"]);
5712
+ var WHAT_MAY_FOLLOW_AN_EVAL = /* @__PURE__ */ new Set(["&&", "||", ";", "\n", "|", "&", ")", "}"]);
5713
+ var evalRunsOneEnvironmentSetup = (pieces, index) => {
5714
+ const opening = pieces[index];
5715
+ const substitution = pieces[index + 1];
5716
+ const closing = pieces[index + 2];
5717
+ const next = pieces[index + 3];
5718
+ return opening !== void 0 && EVAL_BEFORE_ITS_ARGUMENT.test(opening.text) && substitution !== void 0 && SUBSTITUTION_OPENERS.has(substitution.before) && SETS_UP_THE_ENVIRONMENT.test(substitution.text) && closing !== void 0 && SUBSTITUTION_CLOSERS.has(closing.before) && EVAL_AFTER_ITS_ARGUMENT.test(closing.text) && (next === void 0 || WHAT_MAY_FOLLOW_AN_EVAL.has(next.before));
5719
+ };
5720
+ var roughSegmentHasAVisibleConsequence = (toolName, segment, producesHiddenCode, depth) => {
5264
5721
  const innermost = innermostInvocation(segment.invocation);
5265
5722
  const runsAHeredocScript = innermost.program !== void 0 && POSIX_SHELL_PROGRAMS.has(basenameOf(innermost.program.text));
5266
- return invocationHasAVisibleConsequence(toolName, segment.invocation, depth) || sqlClientSyntaxOf(innermost) !== void 0 && (segment.readsInput || segment.feedsASubstitution) || segment.readsAPipe && fetchesCode && runsWhatItReads(innermost) || runsAHeredocScript && segment.heredocs.some((body) => commandHasAVisibleConsequenceAt(toolName, body, depth + 1));
5723
+ return invocationHasAVisibleConsequence(toolName, segment.invocation, depth) || sqlClientSyntaxOf(innermost) !== void 0 && (segment.readsInput || segment.feedsASubstitution) || segment.substitution !== null && basenameOf(innermost.program?.text ?? "") === EVAL && !segment.evalRunsOneEnvironmentSetup || segment.readsAPipe && producesHiddenCode && runsWhatItReads(innermost) || runsAHeredocScript && segment.heredocs.some((body) => commandHasAVisibleConsequenceAt(toolName, body, depth + 1));
5267
5724
  };
5268
5725
  var roughReadingHasAVisibleConsequence = (toolName, command, depth) => {
5269
5726
  const segments = roughSegmentsOf(command);
5270
5727
  const invocations = segments.map((segment) => segment.invocation);
5271
- const fetchesCode = invocations.some(fetches);
5272
- return segments.some((segment) => roughSegmentHasAVisibleConsequence(toolName, segment, fetchesCode, depth)) || runsADownloadedScript(invocations);
5728
+ const producesHiddenCode = invocations.some(producesCodeTheCommandDoesNotShow);
5729
+ return segments.some((segment) => roughSegmentHasAVisibleConsequence(toolName, segment, producesHiddenCode, depth)) || runsADownloadedScript(invocations) || runsInterpreterCodeThatNeedsAPerson(command, invocations, depth);
5273
5730
  };
5274
5731
  var commandHasAVisibleConsequenceAt = (toolName, command, depth) => {
5275
5732
  if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
5276
5733
  const readable = withHeredocSubstitutionsAsQuotedText(command);
5277
5734
  if (classifyDecisionRisk(toolName, readable) === "destructive" || RUNS_FETCHED_CODE.test(readable) || REDIRECTS_INTO_A_SECRET_FILE.test(readable)) return true;
5735
+ if (RUNS_DECODED_CODE.test(readable) || redirectsIntoAFileThatNeedsAPerson(command)) return true;
5278
5736
  const segments = readableCommandSegments(command);
5279
5737
  if (segments === null) return roughReadingHasAVisibleConsequence(toolName, command, depth);
5280
5738
  const invocations = segments.map((segment) => invocationOf(withoutShellGrouping(segment)));
5281
- return invocations.some((invocation) => invocationHasAVisibleConsequence(toolName, invocation, depth)) || pipesIntoASqlClient(command) || pipesFetchedCodeIntoAnInterpreter(command, invocations) || runsADownloadedScript(invocations);
5739
+ return invocations.some((invocation) => invocationHasAVisibleConsequence(toolName, invocation, depth)) || pipesIntoASqlClient(command) || pipesHiddenCodeIntoAnInterpreter(command, invocations) || runsADownloadedScript(invocations) || runsInterpreterCodeThatNeedsAPerson(command, invocations, depth);
5740
+ };
5741
+ var REDIRECTION_OPERATOR_CHARACTER = /[<>]/;
5742
+ var redirectsIntoAFileThatNeedsAPerson = (command) => {
5743
+ if (!REDIRECTION_OPERATOR_CHARACTER.test(command)) return false;
5744
+ const text3 = withHeredocBodiesSetAside(commandForReading(command)).text;
5745
+ const visible = shellVisibleText(text3);
5746
+ return [...text3.matchAll(OUTPUT_REDIRECTION)].some((redirection) => {
5747
+ const operator = (redirection.index ?? 0) + redirection[0].search(REDIRECTION_OPERATOR_CHARACTER);
5748
+ return (visible === null || REDIRECTION_OPERATOR_CHARACTER.test(visible.charAt(operator))) && pathNeedsAPersonToChange((redirection[1] ?? "").replace(QUOTE_CHARACTERS, ""));
5749
+ });
5282
5750
  };
5283
5751
  var posixCommandOf = (words) => words.map(asOneShellWord).join(" ");
5284
5752
  var posixShellJudgeFor = (toolName) => ({
@@ -5289,7 +5757,7 @@ var posixShellJudgeFor = (toolName) => ({
5289
5757
  fetches,
5290
5758
  runsWhatItReads,
5291
5759
  runsScripts,
5292
- namesASecretFile: (fileName) => SECRET_FILE_NAME.test(fileName)
5760
+ namesAFileThatNeedsAPerson: pathNeedsAPersonToChange
5293
5761
  });
5294
5762
  var commandIsDestructive = (toolName, command) => shellDialectOf(toolName) === "powershell" ? powerShellCommandIsDestructive(command, posixShellJudgeFor(toolName)) : readingsThatCanAct(command).some((reading) => classifyDecisionRisk(toolNameForAPosixReading(toolName), reading) === "destructive");
5295
5763
  var commandHasAVisibleConsequence = (toolName, command) => shellDialectOf(toolName) === "powershell" ? powerShellCommandHasAVisibleConsequence(command, posixShellJudgeFor(toolName)) : commandHasAVisibleConsequenceAt(toolNameForAPosixReading(toolName), command, 0);
@@ -6493,7 +6961,7 @@ var unmatchedPolicyOf = (config, toolName) => ({
6493
6961
  mode: config.defaultMode ?? "push_first",
6494
6962
  pushFirstSeconds: config.defaultPushFirstSeconds ?? 20
6495
6963
  });
6496
- var sessionToolGrantCannotCover = (toolName, arg, toolInput) => typeof arg === "string" && commandNeedsAPerson(toolName, arg) || parseMcpToolName(toolName) !== void 0 && (toolInput === void 0 || !mcpToolInputOnlyReads(toolName, toolInput));
6964
+ var sessionToolGrantCannotCover = (toolName, arg, toolInput) => typeof arg === "string" && sessionGrantMustAskAbout(toolName, arg) || parseMcpToolName(toolName) !== void 0 && (toolInput === void 0 || !mcpToolInputOnlyReads(toolName, toolInput));
6497
6965
  var stricterOfTheReadings = (asWritten, asSpelled, unmatched) => comparePolicyStrictness(asWritten?.policy ?? unmatched, asSpelled?.policy ?? unmatched) > 0 ? asWritten : asSpelled;
6498
6966
  var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, sessionId) => {
6499
6967
  const args = toolInput ? policyArgForms(toolName, toolInput, cwd) : [];
@@ -6569,6 +7037,8 @@ var strictestScopeVerdict = (scope, paths) => {
6569
7037
  };
6570
7038
  var survivesDegraded = (config, toolName, toolInputs, cwd, repoKey, sessionId) => toolInputs.length === 1 && resolveAutoResolveOrigin(config, toolName, toolInputs[0], cwd, repoKey, sessionId) === "safe_readonly";
6571
7039
  var FULL_DEFER_MODES = /* @__PURE__ */ new Set(["plan", "auto", "dontAsk"]);
7040
+ var BYPASS_PERMISSION_MODE = "bypassPermissions";
7041
+ var isBypassPermissionMode = (permissionMode) => permissionMode === BYPASS_PERMISSION_MODE;
6572
7042
  var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set([
6573
7043
  "Write",
6574
7044
  "Edit",
@@ -6580,7 +7050,7 @@ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
6580
7050
  if (typeof permissionMode !== "string") return false;
6581
7051
  if (FULL_DEFER_MODES.has(permissionMode)) return true;
6582
7052
  if (permissionMode === "acceptEdits" && FILE_EDIT_TOOLS.has(toolName)) return true;
6583
- if (permissionMode === "bypassPermissions") return options.deferBypass === true;
7053
+ if (isBypassPermissionMode(permissionMode)) return options.deferBypass === true;
6584
7054
  return false;
6585
7055
  };
6586
7056
  var AGENT_QUESTION_TOOL = "AskUserQuestion";
@@ -6660,6 +7130,17 @@ var PRETOOLUSE_GATED_TOOLS = [
6660
7130
  var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, ...ASKED_IN_EVERY_MODE_TOOLS]);
6661
7131
  var isPermissionRequestOwnedByPreToolUse = (toolName, permissionMode, defers = shouldDeferToNativeMode) => PRETOOLUSE_HANDLED_TOOLS.has(toolName) && (isAskedInEveryMode(toolName) || !defers(permissionMode, toolName));
6662
7132
  var CLAUDE_PRETOOLUSE_MATCHER = [...PRETOOLUSE_HANDLED_TOOLS].join("|");
7133
+ var LINE_BREAK = /[\r\n]/;
7134
+ var bypassModeCovers = (toolName, toolInput) => {
7135
+ const arg = extractPolicyArg(toolName, toolInput);
7136
+ if (SHELL_DIALECT_BY_TOOL.has(toolName)) {
7137
+ return arg !== void 0 && !commandIsDestructive(toolName, arg) && !commandHasAVisibleConsequence(toolName, arg);
7138
+ }
7139
+ if (FILE_EDIT_TOOLS.has(toolName)) return arg !== void 0 && !LINE_BREAK.test(arg) && !fileEditNeedsAPerson(toolName, arg);
7140
+ return mcpToolOnlyReads(toolName) && mcpToolInputOnlyReads(toolName, toolInput);
7141
+ };
7142
+ var aChosenRuleGovernsInEitherRepository = (input) => aChosenRuleGoverns(input) || input.sharedRepoKey !== void 0 && input.sharedRepoKey !== input.repoKey && aChosenRuleGoverns({ ...input, repoKey: input.sharedRepoKey });
7143
+ var bypassModeAllows = (input) => isBypassPermissionMode(input.permissionMode) && !isModeStateDegraded(input.modeState) && noModeOverrideIsActive(input) && input.deletesFiles !== true && input.toolInputs.length > 0 && input.toolInputs.every((toolInput) => bypassModeCovers(input.toolName, toolInput)) && !aChosenRuleGovernsInEitherRepository(input);
6663
7144
  var resolveGate = (input) => {
6664
7145
  const { modeState, config, toolName, toolInputs, cwd, sessionId } = input;
6665
7146
  const repoKey = input.sharedRepoKey ?? input.repoKey;
@@ -6677,8 +7158,9 @@ var resolveGate = (input) => {
6677
7158
  }
6678
7159
  const policy = resolvePolicyAcross(config, toolName, modeState.mode, toolInputs, cwd, repoKey, sessionId);
6679
7160
  const verdict = verdictForPolicy(policy);
6680
- const autoApproves = verdict === "allow";
6681
7161
  const autoDenies = verdict === "deny";
7162
+ const allowedByBypassMode = !autoDenies && verdict !== "allow" && bypassModeAllows(input);
7163
+ const autoApproves = verdict === "allow" || allowedByBypassMode;
6682
7164
  if (autoDenies) return { kind: "deny", policy, reason: `Denied by policy for ${policy.tool}` };
6683
7165
  if (isAskedInEveryMode(toolName)) return { kind: "gate", policy };
6684
7166
  if (isModeStateDegraded(modeState)) {
@@ -6692,7 +7174,7 @@ var resolveGate = (input) => {
6692
7174
  if (!scopeRequiresHuman(scopeVerdict)) {
6693
7175
  return {
6694
7176
  kind: "allow",
6695
- policy,
7177
+ policy: allowedByBypassMode ? autoApprove(policy.tool) : policy,
6696
7178
  source: toolInputs.length === 1 && resolveAutoResolveOrigin(config, toolName, toolInputs[0], cwd, repoKey, sessionId) === "safe_readonly" ? "allowlist" : "policy"
6697
7179
  };
6698
7180
  }
@@ -6718,7 +7200,7 @@ var SCOPE_BATCH_REASON = "This patch exceeds the agreed scope. Split it into sin
6718
7200
  var UNREADABLE_CALL_REASON = "Pushary could not read every target of this call. Use a supported command or a single-file edit.";
6719
7201
  var unapprovedScopeReason = (scopeReason) => `${scopeReason}. Nobody approved this path, so the change was refused.`;
6720
7202
  var NO_RESPONSE_REASON = "No response within timeout";
6721
- var codexRaisesItsOwnPrompts = (permissionMode) => permissionMode !== "bypassPermissions";
7203
+ var codexRaisesItsOwnPrompts = (permissionMode) => !isBypassPermissionMode(permissionMode);
6722
7204
  var codexHasNoPromptOfItsOwn = (scopeReason, permissionMode) => scopeReason !== void 0 || !codexRaisesItsOwnPrompts(permissionMode);
6723
7205
  var CODEX_ASK_PASSES = { kind: "pass" };
6724
7206
  var codexAskUnanswered = (ask) => ({ kind: "deny", reason: unansweredRefusalReason(ask.paused) });
@@ -6743,9 +7225,7 @@ var runsACommandThatNeedsAPerson = ({ toolName, toolInputs }) => SHELL_DIALECT_B
6743
7225
  const command = extractPolicyArg(toolName, toolInput);
6744
7226
  return command !== void 0 && (commandNeedsAPerson(toolName, command) || commandWritesThroughARemoteApi(command));
6745
7227
  });
6746
- var CI_WORKFLOW_FILE = /(?:^|\/)\.github\/workflows\/[^/]+$/i;
6747
- var namesASecretOrWorkflowFile = (path) => SECRET_FILE_NAME.test(basenameOf(path)) || CI_WORKFLOW_FILE.test(normalizeSeparators(path));
6748
- var fileEditNeedsAPerson = (toolName, path) => FILE_EDIT_TOOLS.has(toolName) && namesASecretOrWorkflowFile(path);
7228
+ var fileEditNeedsAPerson = (toolName, path) => FILE_EDIT_TOOLS.has(toolName) && pathNeedsAPersonToChange(path);
6749
7229
  var editsAFileThatNeedsAPerson = ({ toolName, toolInputs }) => toolInputs.some((toolInput) => {
6750
7230
  const path = extractPolicyArg(toolName, toolInput);
6751
7231
  return path !== void 0 && fileEditNeedsAPerson(toolName, path);