pushary 1.9.93 → 1.9.96

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 +15 -0
  2. package/dist/{api-QUARGXSY.js → api-FPEL3PLH.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-WW4OMRZX.js → chunk-2DF4TBGX.js} +1 -1
  45. package/dist/{chunk-JZ2MRWC4.js → chunk-2QFD5XGU.js} +3 -3
  46. package/dist/{chunk-ONRA5ON4.js → chunk-3GPXHHBM.js} +1 -1
  47. package/dist/{chunk-7DOSRSRU.js → chunk-3QS2RCCZ.js} +6 -6
  48. package/dist/{chunk-NHGOCUX7.js → chunk-3QT3DMNX.js} +1 -1
  49. package/dist/{chunk-5TQC2LS6.js → chunk-3X3ALGMP.js} +4 -4
  50. package/dist/{chunk-ATWH6TGY.js → chunk-4BLNOWGZ.js} +23 -19
  51. package/dist/{chunk-HUNGCBCF.js → chunk-6JZNZTGZ.js} +3 -3
  52. package/dist/{chunk-NDPOESVW.js → chunk-6YG5PZZS.js} +1 -1
  53. package/dist/{chunk-Y7AX6BS4.js → chunk-77HBRZJI.js} +3 -3
  54. package/dist/{chunk-HALVTC2N.js → chunk-7CW3K3B5.js} +5 -5
  55. package/dist/{chunk-UGGACQMD.js → chunk-AEOYYLXF.js} +3 -3
  56. package/dist/{chunk-V3LBQFQM.js → chunk-ARDQBF25.js} +1 -1
  57. package/dist/{chunk-LTMMFZHG.js → chunk-B4G4Q2AF.js} +1 -1
  58. package/dist/{chunk-CO6TQ44W.js → chunk-B75SIY5C.js} +3 -3
  59. package/dist/{chunk-OLMKZINV.js → chunk-BDCFB4NV.js} +6 -6
  60. package/dist/{chunk-7PQ66CB3.js → chunk-BKN5E4L5.js} +1 -1
  61. package/dist/{chunk-YLCNW7YN.js → chunk-C4IKRLT4.js} +2 -2
  62. package/dist/{chunk-3JVV6PMG.js → chunk-CLN5W5QP.js} +5 -5
  63. package/dist/{chunk-UNPDFIPQ.js → chunk-CQ3YV6QI.js} +2 -2
  64. package/dist/{chunk-SR44ZNZF.js → chunk-CRJDKGEF.js} +5 -5
  65. package/dist/{chunk-UZRKR6NZ.js → chunk-CTKJVVDL.js} +3 -3
  66. package/dist/{chunk-BETKU76V.js → chunk-DN3QW2TT.js} +3 -3
  67. package/dist/{chunk-FTJ5DQL7.js → chunk-EMVK57K7.js} +2 -2
  68. package/dist/{chunk-GLA6MCOG.js → chunk-EPFI6W2K.js} +1 -1
  69. package/dist/{chunk-7VJ7PEI3.js → chunk-GAZPL742.js} +4 -4
  70. package/dist/{chunk-A2FDS7E3.js → chunk-HOYWYQNC.js} +5 -5
  71. package/dist/{chunk-X3AHEUQ5.js → chunk-HY7G3PWJ.js} +3 -3
  72. package/dist/{chunk-7YB2XRY7.js → chunk-IVYKPYMH.js} +3 -3
  73. package/dist/{chunk-66VEUAN5.js → chunk-J3W32P43.js} +1 -1
  74. package/dist/{chunk-3CJFBWID.js → chunk-JCYXG6LQ.js} +3 -3
  75. package/dist/{chunk-373552A3.js → chunk-JU3JYEUG.js} +4 -4
  76. package/dist/{chunk-RXQT5FTX.js → chunk-KG7GT7UW.js} +1 -1
  77. package/dist/{chunk-JJWZIPDL.js → chunk-KHGW7THQ.js} +4 -4
  78. package/dist/{chunk-4HQTZQ4D.js → chunk-LGJPT33C.js} +2 -2
  79. package/dist/{chunk-MSJXRN22.js → chunk-M52UDSMQ.js} +5 -5
  80. package/dist/{chunk-CQMY74I2.js → chunk-M7ZV5IPA.js} +7 -7
  81. package/dist/{chunk-V7TNHTHH.js → chunk-MSPHJNUL.js} +1 -1
  82. package/dist/{chunk-CSUO5YJG.js → chunk-N2KK2NSY.js} +2 -2
  83. package/dist/{chunk-SPPMPM7F.js → chunk-NGSJHUQN.js} +1 -1
  84. package/dist/{chunk-OC47FGOY.js → chunk-OP2WJEPY.js} +1 -1
  85. package/dist/{chunk-PFHU3CW4.js → chunk-OQY4G7FS.js} +1 -1
  86. package/dist/{chunk-OMN5UCMW.js → chunk-OS32P4RZ.js} +560 -36
  87. package/dist/{chunk-2FL5PLA4.js → chunk-PD2DWO4X.js} +2 -2
  88. package/dist/{chunk-4PD7FP55.js → chunk-PDRPVVZX.js} +2 -2
  89. package/dist/{chunk-RFP4Q5I3.js → chunk-QDS55ODM.js} +11 -11
  90. package/dist/{chunk-ANDG4PUV.js → chunk-R2RCFCFW.js} +6 -6
  91. package/dist/{chunk-VMIWR724.js → chunk-SARU6WNW.js} +1 -1
  92. package/dist/{chunk-2BXWBKVN.js → chunk-SI2CJ2BB.js} +3 -3
  93. package/dist/{chunk-5V2YDO4U.js → chunk-SISYAMDC.js} +4 -4
  94. package/dist/{chunk-XDWJX7LT.js → chunk-T2Z5ITWJ.js} +3 -3
  95. package/dist/{chunk-DV7LLH25.js → chunk-T7X5MQ3H.js} +1 -1
  96. package/dist/{chunk-OFQJHWAS.js → chunk-TAMHTWQZ.js} +10 -10
  97. package/dist/{chunk-NPH4DJAU.js → chunk-TQKVKV76.js} +5 -5
  98. package/dist/{chunk-7GFWYFLO.js → chunk-TTK2DLUS.js} +10 -10
  99. package/dist/{chunk-LANGH7UF.js → chunk-TVGOSEWS.js} +4 -4
  100. package/dist/{chunk-NPZ5L2WQ.js → chunk-TXDFQD2I.js} +1 -1
  101. package/dist/{chunk-CFD3IESN.js → chunk-TZWK2BVY.js} +6 -6
  102. package/dist/{chunk-RXAQOAZI.js → chunk-UFWRBHTV.js} +3 -3
  103. package/dist/{chunk-XTNCXIYR.js → chunk-V7EWOHNV.js} +4 -4
  104. package/dist/{chunk-YT3ENXBT.js → chunk-VTFXPV4U.js} +2 -2
  105. package/dist/{chunk-76ROCOWD.js → chunk-VX27HJF6.js} +7 -7
  106. package/dist/{chunk-H4MIOBC6.js → chunk-WUYB3J3Q.js} +2 -2
  107. package/dist/{chunk-N7CPX5S5.js → chunk-WVDPP7OQ.js} +5 -5
  108. package/dist/{chunk-KJ752DUH.js → chunk-WZADX5VO.js} +6 -6
  109. package/dist/{chunk-2BVVMWVF.js → chunk-XLM2JZBE.js} +1 -1
  110. package/dist/{chunk-NZKH2RMQ.js → chunk-XLRPJHXN.js} +1 -1
  111. package/dist/{chunk-UHS3JZLN.js → chunk-XZHJVGR3.js} +2 -2
  112. package/dist/{chunk-CHVXLS7U.js → chunk-YCYFREHD.js} +2 -2
  113. package/dist/{chunk-PS2K6ZQ3.js → chunk-YGMSDYOH.js} +6 -6
  114. package/dist/{codex-app-cards-switch-XP2H2CZO.js → codex-app-cards-switch-7YTDIMCX.js} +11 -11
  115. package/dist/{codex-desktop-launch-QRFE2GHZ.js → codex-desktop-launch-CICLFWQX.js} +1 -1
  116. package/dist/{codex-session-launch-EUUWALWW.js → codex-session-launch-KW7VUPAH.js} +18 -18
  117. package/dist/{config-WBDMUTD4.js → config-632JSNBM.js} +3 -3
  118. package/dist/{global-upgrade-ZXFLRGGV.js → global-upgrade-BMCLOI2P.js} +10 -10
  119. package/dist/{identity-NUQ75BP7.js → identity-UCVTGQHM.js} +4 -4
  120. package/dist/{live-capabilities-SIQ25SSK.js → live-capabilities-5VPEAE37.js} +5 -5
  121. package/dist/{paths-BGO6PZFZ.js → paths-NCKEVGEI.js} +3 -3
  122. package/dist/{reapply-AD5ATZOO.js → reapply-UPZ2L4ID.js} +28 -28
  123. package/dist/src/index.js +29 -29
  124. package/dist/{upgrade-lock-B5HTMEWT.js → upgrade-lock-J36URBLG.js} +4 -4
  125. package/dist/{usage-YHFALPKW.js → usage-BL3KXNWQ.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);
@@ -4539,11 +4539,13 @@ 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
4541
  var PULL_REQUEST_MERGE_PATH = /(?:^|\/)pulls\/\d+\/merge$/;
4542
+ var PULL_REQUEST_MERGE_PATH_SPELLED_ANY_WAY = /(?:^|\/)pulls\/[^/\s]+\/merge(?:[?#]|$)/;
4542
4543
  var mergesAPullRequest = ({ program, args }) => {
4543
4544
  if (program === void 0 || !PULL_REQUEST_CLIS.has(basenameOf(program.text))) return false;
4544
4545
  const words = args.filter((arg) => !arg.text.startsWith("-"));
4545
4546
  return words.some((word, index) => PULL_REQUEST_NOUNS.has(word.text) && words[index + 1]?.text === MERGE_SUBCOMMAND) || args.some((arg) => PULL_REQUEST_MERGE_PATH.test(arg.text));
4546
4547
  };
4548
+ var mergesAPullRequestByAnyPath = ({ program, args }) => program !== void 0 && PULL_REQUEST_CLIS.has(basenameOf(program.text)) && args.some((arg) => PULL_REQUEST_MERGE_PATH_SPELLED_ANY_WAY.test(arg.text));
4547
4549
  var DEPLOY_AND_SCHEMA_COMMAND_PATTERNS = [
4548
4550
  "Bash(kubectl apply:*)",
4549
4551
  "Bash(kubectl replace:*)",
@@ -4594,13 +4596,131 @@ var DEPLOY_AND_SCHEMA_COMMAND_PATTERNS = [
4594
4596
  "Bash(gcloud run deploy:*)",
4595
4597
  "Bash(gcloud functions deploy:*)"
4596
4598
  ];
4599
+ var RELEASE_AND_CLUSTER_PROGRAMS = /* @__PURE__ */ new Set(["kubectl", "fastlane"]);
4600
+ var RELEASE_AND_CLUSTER_COMMAND_PATTERNS = [
4601
+ "Bash(kubectl patch:*)",
4602
+ "Bash(kubectl edit:*)",
4603
+ "Bash(kubectl set:*)",
4604
+ "Bash(kubectl create:*)",
4605
+ "Bash(kubectl drain:*)",
4606
+ "Bash(kubectl cordon:*)",
4607
+ "Bash(kubectl taint:*)",
4608
+ "Bash(fastlane deliver:*)",
4609
+ "Bash(fastlane supply:*)",
4610
+ "Bash(fastlane pilot upload:*)",
4611
+ "Bash(fastlane upload_to_app_store:*)",
4612
+ "Bash(fastlane upload_to_testflight:*)",
4613
+ "Bash(fastlane upload_to_play_store:*)"
4614
+ ];
4615
+ var SUBMITS_THE_BUILD_TO_A_STORE = /^--auto-submit(?:-with-profile)?(?:=|$)/;
4616
+ var EAS_PROGRAM = /^eas(?:-cli)?(?:@[\w.-]+)?$/;
4617
+ var EAS_COMMANDS_THAT_PUBLISH = /^(?:submit|deploy|update|update:(?:republish|rollback|roll-back-to-embedded)|channel:(?:edit|rollout))$/;
4618
+ var easCommandPublishes = (invocation) => {
4619
+ const words = wordsOfInvocation(invocation);
4620
+ return words.some((word, index) => EAS_PROGRAM.test(basenameOf(word.text)) && EAS_COMMANDS_THAT_PUBLISH.test(words[index + 1]?.text ?? ""));
4621
+ };
4622
+ var publishesAnAppBuild = (invocation) => invocation.args.some((arg) => SUBMITS_THE_BUILD_TO_A_STORE.test(arg.text)) || easCommandPublishes(invocation);
4623
+ var PUSHES_EVERY_TAG = /* @__PURE__ */ new Set(["--tags", "--follow-tags"]);
4624
+ var NAMES_A_TAG = /(?:^|:)\+?refs\/tags\/|^tag$|(?:^|:)v?\d+(?:\.\d+){1,3}$|(?:^|:)v?\d+\.\d+\.\d+[-+][\w.]+$|\/v\d+\.\d+\.\d+(?:\.\d+)?(?:[-+][\w.]+)?$|^v\$/;
4625
+ var TAG_HELD_IN_A_VARIABLE = /^v?\$\{?(?:\w+_)?(?:tag|version)(?:_name)?\}?$/i;
4626
+ var GIT_PUSH = "push";
4627
+ var pushesATag = ({ program, args }) => {
4628
+ if (program === void 0 || basenameOf(program.text) !== GIT) return false;
4629
+ const [subcommand, ...rest] = argsAfterHarmlessGlobalFlags(args);
4630
+ if (subcommand?.text !== GIT_PUSH) return false;
4631
+ const operands = rest.filter((arg) => !FLAG.test(arg.text));
4632
+ 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));
4633
+ };
4597
4634
  var SECRET_FILE_NAME = /^(?:\.env(?:\.(?!example$|sample$|template$)[\w.-]+)?|(?:secrets?|credentials)\.(?:json|ya?ml|env|toml|ini)|credentials)$/i;
4635
+ var CI_WORKFLOW_FILE = /(?:^|\/)\.github\/workflows\/[^/]+$/i;
4636
+ var AGENT_SETTINGS_FILE = new RegExp(String.raw`(?:^|/)(?:${[
4637
+ String.raw`\.claude/settings(?:\.local)?\.json`,
4638
+ String.raw`\.claude\.json`,
4639
+ String.raw`\.codex/(?:config\.toml|hooks\.json)`,
4640
+ String.raw`\.cursor/hooks\.json`,
4641
+ String.raw`\.gemini/(?:settings\.json|config/hooks\.json)`,
4642
+ String.raw`\.qwen/settings\.json`,
4643
+ String.raw`\.factory/hooks\.json`,
4644
+ String.raw`\.hermes/config\.ya?ml`,
4645
+ String.raw`\.config/opencode/(?:opencode\.jsonc?|plugins?/.+)`,
4646
+ String.raw`\.pushary(?:/.+)?`,
4647
+ String.raw`\.git/hooks(?:/.+)?`,
4648
+ String.raw`\.ssh(?:/(?!known_hosts(?:\.old)?$).+)?`
4649
+ ].join("|")})$`, "i");
4650
+ var withoutDotSegments = (path) => {
4651
+ const kept = [];
4652
+ for (const segment of path.split("/")) {
4653
+ if (segment === "..") kept.pop();
4654
+ else if (segment !== "" && segment !== ".") kept.push(segment);
4655
+ }
4656
+ return `${path.startsWith("/") ? "/" : ""}${kept.join("/")}`;
4657
+ };
4658
+ var pathNeedsAPersonToChange = (path) => {
4659
+ const written = withoutDotSegments(normalizeSeparators(path));
4660
+ return SECRET_FILE_NAME.test(basenameOf(written)) || CI_WORKFLOW_FILE.test(written) || AGENT_SETTINGS_FILE.test(written);
4661
+ };
4598
4662
  var REDIRECTS_INTO_A_SECRET_FILE = />>?\s*(?:[^\s;|&<>]*\/)?\.env(?:\.[\w-]+)?(?=$|[\s;|&)])/;
4599
4663
  var COPIES_ONTO_ITS_LAST_OPERAND = /* @__PURE__ */ new Set(["cp", "install", "ln", "rsync", "scp"]);
4600
4664
  var WRITES_EVERY_OPERAND = /* @__PURE__ */ new Set(["mv", "tee", "rm", "unlink", "shred", "truncate"]);
4601
4665
  var EDITS_IN_PLACE = /* @__PURE__ */ new Set(["sed", "gsed", "perl"]);
4602
4666
  var IN_PLACE_FLAG = /^(?:-[A-Za-z]*i|--in-place(?:=.*)?)$/;
4667
+ var PERMISSIONS_OFF_WRITES_EVERY_OPERAND = /* @__PURE__ */ new Set([...WRITES_EVERY_OPERAND, "sponge"]);
4668
+ var PERMISSIONS_OFF_EDITS_IN_PLACE = /* @__PURE__ */ new Set([...EDITS_IN_PLACE, "yq"]);
4669
+ var IN_PLACE_LONG_FLAG = /^--in-?place(?:=|$)/;
4670
+ var IN_PLACE_LETTER = "i";
4671
+ var SED_FLAGS_THAT_TAKE_THE_REST = /* @__PURE__ */ new Set(["e", "f", "l"]);
4672
+ var SHORT_FLAGS_THAT_TAKE_THE_REST = /* @__PURE__ */ new Map([
4673
+ ["sed", SED_FLAGS_THAT_TAKE_THE_REST],
4674
+ ["gsed", SED_FLAGS_THAT_TAKE_THE_REST],
4675
+ ["perl", /* @__PURE__ */ new Set(["M", "m", "I", "e", "E", "x", "F", "C", "d", "D"])]
4676
+ ]);
4677
+ var flagEditsInPlace = (program, flag) => {
4678
+ if (IN_PLACE_LONG_FLAG.test(flag)) return true;
4679
+ if (!SHORT_OPTIONS.test(flag)) return false;
4680
+ const takesTheRest = SHORT_FLAGS_THAT_TAKE_THE_REST.get(program);
4681
+ for (const letter of flag.slice(1)) {
4682
+ if (letter === IN_PLACE_LETTER) return true;
4683
+ if (takesTheRest?.has(letter) === true) return false;
4684
+ }
4685
+ return false;
4686
+ };
4687
+ var MOVE = "mv";
4688
+ var TARGET_DIRECTORY_FLAG = "-t";
4689
+ var TARGET_DIRECTORY_ASSIGNMENT = /^--target-directory=(.+)$/;
4690
+ var BRACE_ALTERNATIVES = /\{([^{},]*(?:,[^{},]*)+)\}/;
4691
+ var MAX_SPELLINGS_OF_A_WORD = 64;
4692
+ var hasABraceGroup = (spelling) => BRACE_ALTERNATIVES.test(spelling);
4693
+ var spellingsOfAWord = (word) => {
4694
+ if (!word.plain) return [word];
4695
+ let spellings = [word.text];
4696
+ for (let index = spellings.findIndex(hasABraceGroup); index !== -1; index = spellings.findIndex(hasABraceGroup)) {
4697
+ const spelling = spellings[index] ?? "";
4698
+ const braces = BRACE_ALTERNATIVES.exec(spelling);
4699
+ if (braces === null) return null;
4700
+ const choices = (braces[1] ?? "").split(",");
4701
+ if (spellings.length - 1 + choices.length > MAX_SPELLINGS_OF_A_WORD) return null;
4702
+ spellings = [...spellings.slice(0, index), ...choices.map((choice) => spelling.replace(braces[0], choice)), ...spellings.slice(index + 1)];
4703
+ }
4704
+ return spellings.map((text3) => ({ ...word, text: text3 }));
4705
+ };
4603
4706
  var namesASecretFile = (word) => word !== void 0 && SECRET_FILE_NAME.test(basenameOf(word.text));
4707
+ var namesAFileThatNeedsAPerson = (word) => word !== void 0 && pathNeedsAPersonToChange(word.text);
4708
+ var RECURSIVE_COPY_FLAG = /^(?:-[A-Za-z]*[rRa][A-Za-z]*|--recursive|--archive)$/;
4709
+ var FOLDER_THAT_NEEDS_A_PERSON = new RegExp(String.raw`(?:^|/)(?:${[
4710
+ String.raw`\.github/workflows`,
4711
+ String.raw`\.claude`,
4712
+ String.raw`\.codex`,
4713
+ String.raw`\.cursor`,
4714
+ String.raw`\.gemini`,
4715
+ String.raw`\.qwen`,
4716
+ String.raw`\.factory`,
4717
+ String.raw`\.hermes`,
4718
+ String.raw`\.pushary`,
4719
+ String.raw`\.git/hooks`,
4720
+ String.raw`\.ssh`,
4721
+ String.raw`\.config/opencode(?:/plugins?)?`
4722
+ ].join("|")})$`, "i");
4723
+ var folderNeedsAPersonToChange = (path) => FOLDER_THAT_NEEDS_A_PERSON.test(withoutDotSegments(normalizeSeparators(path)));
4604
4724
  var NETWORK_SENDERS = /* @__PURE__ */ new Set([
4605
4725
  "curl",
4606
4726
  "wget",
@@ -4629,6 +4749,47 @@ var writesASecretFile = ({ program, args }) => {
4629
4749
  if (WRITES_EVERY_OPERAND.has(name)) return operands.some(namesASecretFile);
4630
4750
  return EDITS_IN_PLACE.has(name) && args.some((arg) => IN_PLACE_FLAG.test(arg.text)) && operands.some(namesASecretFile);
4631
4751
  };
4752
+ var referencesAFileThatNeedsAPerson = (word) => pathNeedsAPersonToChange(word.text.replace(FILE_REFERENCE_PREFIX, ""));
4753
+ var sendsAFileThatNeedsAPerson = ({ program, args }) => program !== void 0 && NETWORK_SENDERS.has(basenameOf(program.text)) && args.some(referencesAFileThatNeedsAPerson);
4754
+ var targetDirectoryOf = (args) => {
4755
+ const flag = args.findIndex((arg) => arg.text === TARGET_DIRECTORY_FLAG);
4756
+ if (flag !== -1) return args[flag + 1]?.text;
4757
+ return args.map((arg) => TARGET_DIRECTORY_ASSIGNMENT.exec(arg.text)?.[1]).find((directory) => directory !== void 0);
4758
+ };
4759
+ var copiesOntoAFileThatNeedsAPerson = (args, operands) => {
4760
+ const targetDirectory = targetDirectoryOf(args);
4761
+ const destination = targetDirectory ?? operands.at(-1)?.text;
4762
+ if (destination === void 0) return false;
4763
+ const sources = targetDirectory === void 0 ? operands.slice(0, -1) : operands.filter((operand) => operand.text !== targetDirectory);
4764
+ const landingPaths = [destination, ...sources.map((source) => `${destination}/${basenameOf(source.text)}`)];
4765
+ return landingPaths.some(pathNeedsAPersonToChange) || args.some((arg) => RECURSIVE_COPY_FLAG.test(arg.text)) && landingPaths.some(folderNeedsAPersonToChange);
4766
+ };
4767
+ var GIT_HOOKS_PATH_SETTING = /^core\.hookspath(?:=|$)/i;
4768
+ var GIT_ONE_COMMAND_SETTING_FLAG = "-c";
4769
+ var GIT_CONFIG_SUBCOMMAND = "config";
4770
+ var GIT_CONFIG_UNSET_FLAGS = /* @__PURE__ */ new Set(["--unset", "--unset-all"]);
4771
+ var changesWhereGitLooksForHooks = ({ program, args }) => {
4772
+ if (program === void 0 || basenameOf(program.text) !== GIT) return false;
4773
+ const setting = args.findIndex((arg) => GIT_HOOKS_PATH_SETTING.test(arg.text));
4774
+ if (setting === -1) return false;
4775
+ if (args[setting - 1]?.text === GIT_ONE_COMMAND_SETTING_FLAG) return true;
4776
+ return args.some((arg) => arg.text === GIT_CONFIG_SUBCOMMAND) && (args[setting + 1] !== void 0 || args.some((arg) => GIT_CONFIG_UNSET_FLAGS.has(arg.text)));
4777
+ };
4778
+ var writesAFileThatNeedsAPerson = ({ program, args }) => {
4779
+ if (program === void 0) return false;
4780
+ if (sendsAFileThatNeedsAPerson({ program, args })) return true;
4781
+ const name = basenameOf(program.text);
4782
+ const editsInPlace = PERMISSIONS_OFF_EDITS_IN_PLACE.has(name) && args.some((arg) => flagEditsInPlace(name, arg.text));
4783
+ if (!COPIES_ONTO_ITS_LAST_OPERAND.has(name) && !PERMISSIONS_OFF_WRITES_EVERY_OPERAND.has(name) && !editsInPlace) return false;
4784
+ const spelled = args.filter((arg) => !FLAG.test(arg.text)).map(spellingsOfAWord);
4785
+ if (spelled.includes(null)) return true;
4786
+ const operands = spelled.flatMap((words) => words ?? []);
4787
+ if (COPIES_ONTO_ITS_LAST_OPERAND.has(name)) return copiesOntoAFileThatNeedsAPerson(args, operands);
4788
+ if (PERMISSIONS_OFF_WRITES_EVERY_OPERAND.has(name)) {
4789
+ return operands.some((operand) => namesAFileThatNeedsAPerson(operand) || folderNeedsAPersonToChange(operand.text)) || name === MOVE && copiesOntoAFileThatNeedsAPerson(args, operands);
4790
+ }
4791
+ return operands.some(namesAFileThatNeedsAPerson);
4792
+ };
4632
4793
  var GITHUB_COMMANDS_THAT_ONLY_READ = /* @__PURE__ */ new Set([
4633
4794
  "search",
4634
4795
  "browse",
@@ -4734,6 +4895,7 @@ var AWS_OPTIONS_WITH_VALUES = /* @__PURE__ */ new Set([
4734
4895
  ]);
4735
4896
  var AWS_LOCAL_COMMANDS = /* @__PURE__ */ new Set(["configure", "help", "history", "sso", "login", "logout"]);
4736
4897
  var AWS_READ_OPERATION = /^(?:describe|list|get|head|batch-get|lookup|search|filter|scan|query|select|wait|validate|estimate|simulate|tail|ls|presign|help)(?:-|$)/;
4898
+ var AWS_SIGN_IN_OR_QUERY_OPERATION = /^(?:login|update-kubeconfig|start-query|stop-query|assume-role)(?:-|$)/;
4737
4899
  var AWS_S3_SERVICE = "s3";
4738
4900
  var AWS_S3_COPIES = /* @__PURE__ */ new Set(["cp", "sync"]);
4739
4901
  var S3_URL_PREFIX = "s3://";
@@ -4803,11 +4965,16 @@ var githubCommandOnlyReads = (noun, verb) => {
4803
4965
  const readVerbs = GITHUB_READ_COMMANDS.get(noun.text);
4804
4966
  return readVerbs !== void 0 && (verb === void 0 || readVerbs.has(verb.text));
4805
4967
  };
4806
- var githubCliWrites = (args) => {
4968
+ var githubCommandWordsOf = (args) => {
4807
4969
  const meaningful = argsAfterHarmlessGlobalFlags(args);
4808
4970
  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);
4971
+ return { meaningful, noun, verb };
4972
+ };
4973
+ var githubApiArgsOf = ({ meaningful, noun }) => noun === void 0 ? [] : meaningful.slice(meaningful.indexOf(noun) + 1);
4974
+ var githubCliWrites = (args) => {
4975
+ const words = githubCommandWordsOf(args);
4976
+ if (words.noun?.text === GITHUB_API_SUBCOMMAND) return githubApiRequestWrites(githubApiArgsOf(words));
4977
+ return !githubCommandOnlyReads(words.noun, words.verb);
4811
4978
  };
4812
4979
  var stripeCliWrites = (args) => {
4813
4980
  const [command, operation] = positionalWordsOf(args, STRIPE_OPTIONS_WITH_VALUES);
@@ -4826,16 +4993,208 @@ var twilioCliWrites = (args) => {
4826
4993
  if (topic === void 0 || !topic.text.includes(TWILIO_TOPIC_SEPARATOR)) return false;
4827
4994
  return !TWILIO_READ_ACTIONS.has(topic.text.split(TWILIO_TOPIC_SEPARATOR).at(-1) ?? "");
4828
4995
  };
4996
+ var GITHUB_CLI = "gh";
4997
+ var AWS_CLI = "aws";
4998
+ var awsOnlySignsInOrQueries = (args) => {
4999
+ const [service, operation] = positionalWordsOf(args, AWS_OPTIONS_WITH_VALUES);
5000
+ return service !== void 0 && service.text !== AWS_S3_SERVICE && operation !== void 0 && AWS_SIGN_IN_OR_QUERY_OPERATION.test(operation.text);
5001
+ };
4829
5002
  var REMOTE_API_CLI_WRITES = /* @__PURE__ */ new Map([
4830
- ["gh", githubCliWrites],
5003
+ [GITHUB_CLI, githubCliWrites],
4831
5004
  ["stripe", stripeCliWrites],
4832
- ["aws", awsCliWrites],
5005
+ [AWS_CLI, awsCliWrites],
4833
5006
  ["twilio", twilioCliWrites]
4834
5007
  ]);
4835
5008
  var invocationWritesThroughARemoteApi = ({ program, args }) => {
4836
5009
  const writes = program === void 0 ? void 0 : REMOTE_API_CLI_WRITES.get(basenameOf(program.text));
4837
5010
  return writes !== void 0 && writes(args);
4838
5011
  };
5012
+ var GITHUB_REPO_NOUN = "repo";
5013
+ var GITHUB_EDIT_VERB = "edit";
5014
+ var GITHUB_CREATE_VERB = "create";
5015
+ var GITHUB_PUBLIC_OPTION = /^--public(?:=(?!false$).*)?$/;
5016
+ var GITHUB_VISIBILITY_OPTION = /^--visibility(?:=|$)/;
5017
+ var GITHUB_COMMANDS_WITH_A_CONSEQUENCE = /* @__PURE__ */ new Map([
5018
+ ["secret", /* @__PURE__ */ new Set(["set"])],
5019
+ ["variable", /* @__PURE__ */ new Set(["set"])],
5020
+ [GITHUB_REPO_NOUN, /* @__PURE__ */ new Set(["archive", "rename"])],
5021
+ ["ssh-key", /* @__PURE__ */ new Set(["add"])],
5022
+ ["gpg-key", /* @__PURE__ */ new Set(["add"])]
5023
+ ]);
5024
+ var HTTP_METHODS_THAT_CHANGE_A_RESOURCE = /* @__PURE__ */ new Set(["DELETE", "PUT", "PATCH"]);
5025
+ var GITHUB_API_ENDPOINTS_FOR_EVERYDAY_WORK = /(?:^|\/)(?:issues|pulls|comments|notifications|labels|assignees|reviews|reactions|requested_reviewers)(?:\/|$)/;
5026
+ var GITHUB_API_ENDPOINTS_THAT_PUBLISH = /(?:^|\/)(?:merges|releases|dispatches|transfer|keys|deployments|hooks|git\/refs)\/?$/;
5027
+ var GRAPHQL_QUERY_FIELD = /^query=/;
5028
+ var GRAPHQL_FIELD_WORDS_THAT_DELETE_OR_PUBLISH = /delete|archive|transfer|merge|branchprotectionrule/;
5029
+ var GRAPHQL_FIELDS_THAT_PUBLISH = /* @__PURE__ */ new Set([
5030
+ "updaterepository",
5031
+ "createref",
5032
+ "updateref",
5033
+ "updaterefs",
5034
+ "createcommitonbranch"
5035
+ ]);
5036
+ var WORDS_AND_THE_TEXT_BETWEEN = /(\W+)/;
5037
+ var OPENS_A_CALL = /^\s*\(/;
5038
+ var GRAPHQL_STRING = /"(?:[^"\\]|\\.)*"/g;
5039
+ var graphqlFieldsCalledIn = (query) => {
5040
+ const parts = query.replace(GRAPHQL_STRING, '""').split(WORDS_AND_THE_TEXT_BETWEEN);
5041
+ return parts.filter((part, index) => index % 2 === 0 && part !== "" && OPENS_A_CALL.test(parts[index + 1] ?? "")).map((field) => field.toLowerCase());
5042
+ };
5043
+ var graphqlFieldDeletesOrPublishes = (field) => GRAPHQL_FIELDS_THAT_PUBLISH.has(field) || GRAPHQL_FIELD_WORDS_THAT_DELETE_OR_PUBLISH.test(field);
5044
+ var SINGLE_SHORT_OPTION = /^-[A-Za-z]$/;
5045
+ 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));
5046
+ var githubApiEndpointOf = (args) => (args.find((arg, index) => !FLAG.test(arg.text) && !takesTheNextGithubApiWord(args[index - 1]?.text ?? ""))?.text ?? "").replace(URL_QUERY_OR_FRAGMENT2, "");
5047
+ var graphqlQueriesOf = (options) => options.flatMap(({ name, value }) => value !== void 0 && GITHUB_API_BODY_OPTIONS.has(name) && GRAPHQL_QUERY_FIELD.test(value.text) ? [value] : []);
5048
+ var graphqlQueryHasAConsequence = (query) => query.expands || GITHUB_API_FILE_FIELD.test(query.text) || GRAPHQL_MUTATION.test(query.text) && graphqlFieldsCalledIn(query.text).some(graphqlFieldDeletesOrPublishes);
5049
+ var graphqlRequestHasAConsequence = (options) => {
5050
+ const queries = graphqlQueriesOf(options);
5051
+ return queries.length === 0 || queries.some(graphqlQueryHasAConsequence);
5052
+ };
5053
+ var HTTP_DELETE = "DELETE";
5054
+ var githubApiMethodHasAConsequence = (method, endpoint) => {
5055
+ if (method.expands) return true;
5056
+ const name = method.text.toUpperCase();
5057
+ return name === HTTP_DELETE || HTTP_METHODS_THAT_CHANGE_A_RESOURCE.has(name) && !GITHUB_API_ENDPOINTS_FOR_EVERYDAY_WORK.test(endpoint);
5058
+ };
5059
+ var githubApiRequestHasAConsequence = (args) => {
5060
+ const options = githubApiOptionsOf(args);
5061
+ if (options === null) return true;
5062
+ const endpoint = githubApiEndpointOf(args);
5063
+ if (endpoint === GITHUB_GRAPHQL_ENDPOINT) return graphqlRequestHasAConsequence(options);
5064
+ if (GITHUB_API_ENDPOINTS_THAT_PUBLISH.test(endpoint)) return githubApiRequestWrites(args);
5065
+ const method = options.filter((option) => HTTP_METHOD_OPTIONS.has(option.name)).at(-1)?.value;
5066
+ return method !== void 0 && githubApiMethodHasAConsequence(method, endpoint);
5067
+ };
5068
+ var githubCommandHasAConsequence = ({ program, args }) => {
5069
+ if (program === void 0 || basenameOf(program.text) !== GITHUB_CLI) return false;
5070
+ const words = githubCommandWordsOf(args);
5071
+ const { noun, verb } = words;
5072
+ if (noun === void 0) return false;
5073
+ if (noun.text === GITHUB_API_SUBCOMMAND) return githubApiRequestHasAConsequence(githubApiArgsOf(words));
5074
+ if (noun.text === GITHUB_REPO_NOUN && verb?.text === GITHUB_EDIT_VERB) return words.meaningful.some((arg) => GITHUB_VISIBILITY_OPTION.test(arg.text));
5075
+ if (noun.text === GITHUB_REPO_NOUN && verb?.text === GITHUB_CREATE_VERB) return words.meaningful.some((arg) => GITHUB_PUBLIC_OPTION.test(arg.text));
5076
+ return verb !== void 0 && (GITHUB_COMMANDS_WITH_A_CONSEQUENCE.get(noun.text)?.has(verb.text) ?? false);
5077
+ };
5078
+ var STRIPE_CLI = "stripe";
5079
+ var STRIPE_LIVE_MODE = /^--live$|[sr]k_live_/;
5080
+ var STRIPE_API_KEY_OPTION = "--api-key";
5081
+ var STRIPE_API_KEY_HELD_IN_A_VARIABLE = /^(?:--api-key|STRIPE_API_KEY)=.*\$/;
5082
+ 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);
5083
+ var writesThroughAServiceApi = (invocation) => {
5084
+ const innermost = innermostInvocation(invocation);
5085
+ const name = basenameOf(innermost.program?.text ?? "");
5086
+ if (name === GITHUB_CLI) return false;
5087
+ if (name === STRIPE_CLI && !stripeMayUseLiveData(wordsOfInvocation(invocation))) return false;
5088
+ if (name === AWS_CLI && awsOnlySignsInOrQueries(innermost.args)) return false;
5089
+ return invocationWritesThroughARemoteApi(innermost);
5090
+ };
5091
+ var NO_SHORT_OPTIONS = /* @__PURE__ */ new Set();
5092
+ var HTTP_CLIENT_SYNTAX = /* @__PURE__ */ new Map([
5093
+ ["curl", {
5094
+ bodyOption: /^--(?:data(?:-[a-z]+)?|json|form(?:-string)?|upload-file)(?:=|$)/,
5095
+ methodOption: /^--request(?:=|$)/,
5096
+ getOption: /^--get$/,
5097
+ shortOptionsWithAValue: /* @__PURE__ */ new Set([..."AbcCdDeEFHKmoPQrtTuUwxXyYz"]),
5098
+ shortBodyOptions: /* @__PURE__ */ new Set(["d", "F", "T"]),
5099
+ shortMethodOption: "X",
5100
+ shortGetOption: "G"
5101
+ }],
5102
+ ["wget", {
5103
+ bodyOption: /^--(?:post-(?:data|file)|body-(?:data|file))(?:=|$)/,
5104
+ methodOption: /^--method(?:=|$)/,
5105
+ getOption: null,
5106
+ shortOptionsWithAValue: NO_SHORT_OPTIONS,
5107
+ shortBodyOptions: NO_SHORT_OPTIONS,
5108
+ shortMethodOption: null,
5109
+ shortGetOption: null
5110
+ }]
5111
+ ]);
5112
+ var SHORT_OPTIONS = /^-[^-]/;
5113
+ var optionValueAt = (args, index, attached) => {
5114
+ const arg = args[index];
5115
+ return attached === "" || arg === void 0 ? args[index + 1] : { ...arg, text: attached };
5116
+ };
5117
+ var readHttpRequest = (args, syntax) => {
5118
+ let sendsABody = false;
5119
+ let forcesAGet = false;
5120
+ const methods = [];
5121
+ args.forEach((arg, index) => {
5122
+ if (LONG_OPTION.test(arg.text)) {
5123
+ sendsABody ||= syntax.bodyOption.test(arg.text);
5124
+ forcesAGet ||= syntax.getOption?.test(arg.text) ?? false;
5125
+ const separator = arg.text.indexOf(FLAG_VALUE_SEPARATOR);
5126
+ const method = syntax.methodOption.test(arg.text) ? optionValueAt(args, index, separator === -1 ? "" : arg.text.slice(separator + 1)) : void 0;
5127
+ if (method !== void 0) methods.push(method);
5128
+ return;
5129
+ }
5130
+ if (!SHORT_OPTIONS.test(arg.text)) return;
5131
+ for (let position = 1; position < arg.text.length; position += 1) {
5132
+ const letter = arg.text.charAt(position);
5133
+ forcesAGet ||= letter === syntax.shortGetOption;
5134
+ if (!syntax.shortOptionsWithAValue.has(letter)) continue;
5135
+ sendsABody ||= syntax.shortBodyOptions.has(letter);
5136
+ const method = letter === syntax.shortMethodOption ? optionValueAt(args, index, arg.text.slice(position + 1)) : void 0;
5137
+ if (method !== void 0) methods.push(method);
5138
+ return;
5139
+ }
5140
+ });
5141
+ return { sendsABody, forcesAGet, methods };
5142
+ };
5143
+ var requestSendsData = ({ sendsABody, forcesAGet, methods }) => methods.length === 0 ? sendsABody && !forcesAGet : methods.some((method) => method.expands || !HTTP_READ_METHODS.has(method.text.toUpperCase()));
5144
+ 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;
5145
+ var ROUTES_THE_REQUEST_ELSEWHERE = /^(?:--(?:resolve|connect-to|proxy[\w.]*|preproxy|socks[\w-]*|config)(?:=|$)|-[A-Za-z]*[xK])/;
5146
+ var URL_WITH_A_SCHEME = /^[a-z][a-z\d+.-]*:\/\//i;
5147
+ var requestStaysOnThisMachine = (args) => {
5148
+ if (args.some((arg) => ROUTES_THE_REQUEST_ELSEWHERE.test(arg.text))) return false;
5149
+ const addresses = args.filter((arg) => !FLAG.test(arg.text) && (URL_WITH_A_SCHEME.test(arg.text) || THIS_MACHINE_ADDRESS.test(arg.text)));
5150
+ return addresses.length > 0 && addresses.every((arg) => THIS_MACHINE_ADDRESS.test(arg.text));
5151
+ };
5152
+ var sendsDataOffThisMachine = ({ program, args }) => {
5153
+ const syntax = program === void 0 ? void 0 : HTTP_CLIENT_SYNTAX.get(basenameOf(program.text));
5154
+ return syntax !== void 0 && requestSendsData(readHttpRequest(args, syntax)) && !requestStaysOnThisMachine(args);
5155
+ };
5156
+ var KUBECTL = "kubectl";
5157
+ var KUBECTL_EXEC = "exec";
5158
+ var kubectlExecRunsMoreThanAReadOnlyCommand = ({ program, args }) => {
5159
+ if (program === void 0 || basenameOf(program.text) !== KUBECTL) return false;
5160
+ const endOfOptions = args.findIndex((arg) => arg.text === END_OF_OPTIONS3);
5161
+ const kubectlWords = endOfOptions === -1 ? args : args.slice(0, endOfOptions);
5162
+ if (!kubectlWords.some((arg) => arg.text === KUBECTL_EXEC)) return false;
5163
+ const command = endOfOptions === -1 ? [] : args.slice(endOfOptions + 1);
5164
+ return command.length === 0 || command.some((arg) => arg.expands) || !isSafeReadOnlyCommand(posixCommandOf(command.map((arg) => arg.text)));
5165
+ };
5166
+ var SSH = "ssh";
5167
+ var SSH_OPTION_LETTERS_WITH_A_VALUE = /* @__PURE__ */ new Set([..."BbcDEeFIiJLlmOoPpQRSWw"]);
5168
+ var SSH_SHORT_OPTION_CLUSTER = /^-[A-Za-z]+$/;
5169
+ var SSH_OPTION_THAT_RUNS_A_COMMAND = /(?:proxy|local|remote|knownhosts)command/i;
5170
+ var SSH_CONFIG_OPTION = "-o";
5171
+ var sshOptionTakesTheNextWord = (text3) => {
5172
+ if (!SSH_SHORT_OPTION_CLUSTER.test(text3)) return false;
5173
+ const letters = text3.slice(1);
5174
+ const firstValueOption = [...letters].findIndex((letter) => SSH_OPTION_LETTERS_WITH_A_VALUE.has(letter));
5175
+ return firstValueOption === letters.length - 1;
5176
+ };
5177
+ 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));
5178
+ var sshRemoteCommandOf = (args) => {
5179
+ let index = 0;
5180
+ while (index < args.length) {
5181
+ const text3 = args[index]?.text ?? "";
5182
+ if (text3 === END_OF_OPTIONS3) {
5183
+ index += 1;
5184
+ break;
5185
+ }
5186
+ if (!FLAG.test(text3)) break;
5187
+ index += sshOptionTakesTheNextWord(text3) ? 2 : 1;
5188
+ }
5189
+ return args.slice(index + 1);
5190
+ };
5191
+ var sshRunsMoreThanAReadOnlyCommand = ({ program, args }) => {
5192
+ if (program === void 0 || basenameOf(program.text) !== SSH) return false;
5193
+ if (sshOptionRunsACommand(args)) return true;
5194
+ const command = sshRemoteCommandOf(args);
5195
+ return command.length > 0 && (command.some((arg) => arg.expands) || !isSafeReadOnlyCommand(command.map((arg) => arg.text).join(" ")));
5196
+ };
5197
+ var changesSomethingHostedElsewhere = (invocation) => githubCommandHasAConsequence(invocation) || writesThroughAServiceApi(invocation) || sendsDataOffThisMachine(invocation) || kubectlExecRunsMoreThanAReadOnlyCommand(invocation) || sshRunsMoreThanAReadOnlyCommand(invocation);
4839
5198
  var segmentWritesThroughARemoteApi = (segment, depth) => {
4840
5199
  const innermost = innermostInvocation(segment.invocation);
4841
5200
  return invocationWritesThroughARemoteApi(innermost) || commandsNestedIn(innermost, inlineScriptsOf(innermost, segment.heredocs)).some((nested) => commandWritesThroughARemoteApiAt(nested, depth + 1));
@@ -4964,12 +5323,12 @@ var FIND_RUNNERS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"
4964
5323
  var FIND_RUNNER_END = /* @__PURE__ */ new Set([";", "+"]);
4965
5324
  var FIND = "find";
4966
5325
  var commandsRunByFind = ({ program, args }) => program === void 0 || basenameOf(program.text) !== FIND ? [] : args.flatMap((arg, index) => FIND_RUNNERS.has(arg.text) ? [{ program: args[index + 1], args: args.slice(index + 2, findRunnerEnd(args, index + 2)) }] : []);
4967
- var losesDataVisibly = (toolName, invocation, normalized, depth) => {
5326
+ var losesDataVisibly = (toolName, invocation, normalized, depth, line) => {
4968
5327
  const { program, args } = invocation;
4969
5328
  if (program === void 0) return false;
4970
5329
  const name = basenameOf(program.text);
4971
5330
  if (name === FIND) {
4972
- return args.some((arg) => arg.text === FIND_DELETE) || commandsRunByFind(invocation).some((run) => invocationHasAVisibleConsequence(toolName, run, depth + 1));
5331
+ return args.some((arg) => arg.text === FIND_DELETE) || commandsRunByFind(invocation).some((run) => invocationHasAVisibleConsequence(toolName, run, depth + 1, line));
4973
5332
  }
4974
5333
  const destructiveArguments = destructiveArgumentsOf(name, args);
4975
5334
  return destructiveArguments !== void 0 && args.some((arg) => namesDestructiveArgument(arg.text, destructiveArguments)) || namesDestructiveWordInAnyCase(name, args.map((arg) => arg.text)) || gitDiscardsWork(invocation) || changesAService(invocation) || replacesTheCrontab(invocation) || isCoveredByPolicy(VISIBLE_DATA_LOSS_PATTERNS, toolName, normalized);
@@ -5134,15 +5493,18 @@ var honoursPreviewFlags = ({ program, args }) => {
5134
5493
  };
5135
5494
  var onlyPreviews = (invocation) => invocation.program !== void 0 && honoursPreviewFlags(invocation) && invocation.args.some((arg) => !arg.expands && PREVIEW_FLAG.test(arg.text));
5136
5495
  var changesWhatIsDeployed = (toolName, invocation, normalized) => !onlyPreviews(invocation) && (changesProduction(invocation) || mergesAPullRequest(invocation) || isCoveredByPolicy(DEPLOY_AND_SCHEMA_COMMAND_PATTERNS, toolName, normalized));
5137
- var invocationHasAVisibleConsequence = (toolName, invocation, depth) => {
5496
+ 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));
5497
+ var invocationHasAConsequenceOnlyPermissionsOffAsksAbout = (toolName, invocation, normalized) => releasesOrChangesACluster(toolName, invocation, normalized) || mergesAPullRequestByAnyPath(invocation) || writesAFileThatNeedsAPerson(invocation) || changesWhereGitLooksForHooks(invocation) || changesSomethingHostedElsewhere(invocation);
5498
+ var invocationHasAVisibleConsequence = (toolName, invocation, depth, line) => {
5138
5499
  if (depth > MAX_CONSEQUENCE_DEPTH) return true;
5139
5500
  if (invocation.program === void 0) return false;
5140
5501
  const normalized = quotedInvocation(invocation);
5141
5502
  const innermost = innermostInvocation(invocation);
5142
5503
  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));
5504
+ return classifyDecisionRisk(toolName, normalized) === "destructive" || changesWhatIsDeployed(toolName, invocation, normalized) || writesASecretFile(invocation) || line === "permissions_off" && invocationHasAConsequenceOnlyPermissionsOffAsksAbout(toolName, invocation, normalized) || losesDataVisibly(toolName, invocation, normalized, depth, line) || subcommandDestroysVisibly(invocation) || sqlClientChangesData(invocation) || innermost !== invocation && invocationHasAVisibleConsequence(toolName, innermost, depth, line) || script === null && passesAnInlineScript(invocation) || script !== null && commandHasAVisibleConsequenceAt(toolName, script, depth + 1, line) || readingsPastGlobalFlags(invocation).some((reading) => invocationHasAVisibleConsequence(toolName, reading, depth + 1, line));
5144
5505
  };
5145
5506
  var RUNS_FETCHED_CODE = /(?:\$\(|`|<\()\s*(?:curl|wget)\b/;
5507
+ var RUNS_DECODED_CODE = /(?:^|[;&|(\s])(?:bash|sh|zsh|source|\.|python3?|node|ruby|perl)\s+(?:-[A-Za-z]+\s+)*["']?(?:<\(|\$\()[^)]*\b(?:base64|xxd|openssl)\b/;
5146
5508
  var FETCHING_PROGRAMS = /* @__PURE__ */ new Set(["curl", "wget"]);
5147
5509
  var READS_ITS_SCRIPT_FROM_STANDARD_INPUT = /^-[A-Za-z]*s[A-Za-z]*$/;
5148
5510
  var interpreterNameOf = (program) => basenameOf(program.text).replace(/\d+(?:\.\d+)*$/, "");
@@ -5162,7 +5524,79 @@ var runsADownloadedScript = (invocations) => {
5162
5524
  };
5163
5525
  var pipedSegmentsOf = (command) => (shellSegmentsReadingAPipe(commandForReading(command)) ?? []).map((segment) => innermostInvocation(invocationOf(withoutShellGrouping(segment))));
5164
5526
  var pipesIntoASqlClient = (command) => pipedSegmentsOf(command).some((invocation) => sqlClientSyntaxOf(invocation) !== void 0);
5165
- var pipesFetchedCodeIntoAnInterpreter = (command, invocations) => invocations.some(fetches) && pipedSegmentsOf(command).some(runsWhatItReads);
5527
+ var DECODING_FLAGS_BY_PROGRAM = /* @__PURE__ */ new Map([
5528
+ ["base64", /^(?:-[A-Za-z]*[dD]|--decode)$/],
5529
+ ["xxd", /^-[A-Za-z]*r/],
5530
+ ["openssl", /^-d$/]
5531
+ ]);
5532
+ var decodes = (invocation) => {
5533
+ const { program, args } = innermostInvocation(invocation);
5534
+ const decodingFlags = program === void 0 ? void 0 : DECODING_FLAGS_BY_PROGRAM.get(basenameOf(program.text));
5535
+ return decodingFlags !== void 0 && args.some((arg) => decodingFlags.test(arg.text));
5536
+ };
5537
+ var producesCodeTheCommandDoesNotShow = (invocation) => fetches(invocation) || decodes(invocation);
5538
+ var pipesCodeIntoAnInterpreter = (command, invocations, producesCode) => invocations.some(producesCode) && pipedSegmentsOf(command).some(runsWhatItReads);
5539
+ var CODE_THAT_DELETES_RECURSIVELY = /\b(?:rmtree|rimraf|rm_rf|rm_r|removedirs)\b|\b(?:rm|rmSync|remove|rmdir|rmdirSync)\s*\([^)]*recursive/;
5540
+ var CODE_THAT_DECODES = /\b(?:b64decode|atob|base64)\b/;
5541
+ var CODE_THAT_RUNS_CODE_IT_BUILDS = /(?<![.\w])(?:eval|exec)\s*\(/;
5542
+ var CODE_THAT_RUNS_COMMANDS = /\b(?:execSync|execFileSync|spawnSync|execFile|spawn|exec|child_process|subprocess|popen|Popen|system)\b/;
5543
+ 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]/;
5544
+ var CODE_STRING_LITERAL = /'([^'\\\n]*)'|"([^"\\\n]*)"|`([^`\\\n]*)`/g;
5545
+ var CODE_PATH_DIVISION = /'\s*\/\s*'/g;
5546
+ var DOUBLE_QUOTE2 = /"/g;
5547
+ var PATH_WITH_A_DOT = /\./;
5548
+ var LONGEST_PATH_SPELLED_IN_PARTS = 4;
5549
+ var SHELL_COMMAND_IN_A_STRING = /\S\s+\S/;
5550
+ var stringLiteralsOf = (code) => [...code.matchAll(CODE_STRING_LITERAL)].map((literal) => literal[1] ?? literal[2] ?? literal[3] ?? "");
5551
+ var pathsSpelledIn = (code) => {
5552
+ const literals = stringLiteralsOf(code.replace(DOUBLE_QUOTE2, "'").replace(CODE_PATH_DIVISION, "/"));
5553
+ 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));
5554
+ };
5555
+ var commandsSpelledIn = (code) => [
5556
+ ...stringLiteralsOf(code).filter((literal) => SHELL_COMMAND_IN_A_STRING.test(literal)),
5557
+ ...code.split("\n").map((line) => stringLiteralsOf(line).join(" ")).filter((line) => SHELL_COMMAND_IN_A_STRING.test(line))
5558
+ ];
5559
+ var INTERPRETERS_THAT_TAKE_INLINE_CODE = /* @__PURE__ */ new Set([...INLINE_CODE_INTERPRETERS, "bun", "deno", "tsx", "ts-node"]);
5560
+ var RUNNERS_OF_AN_INTERPRETER = /* @__PURE__ */ new Set([
5561
+ "uv",
5562
+ "poetry",
5563
+ "pnpm",
5564
+ "npm",
5565
+ "yarn",
5566
+ "bundle",
5567
+ "pipenv",
5568
+ "hatch",
5569
+ "pdm",
5570
+ "rye",
5571
+ "conda",
5572
+ "mamba"
5573
+ ]);
5574
+ var isAnInterpreter = (word) => INTERPRETERS_THAT_TAKE_INLINE_CODE.has(interpreterNameOf(word));
5575
+ var interpreterInvocationOf = (invocation) => {
5576
+ const innermost = innermostInvocation(invocation);
5577
+ const { program, args } = innermost;
5578
+ if (program === void 0) return [];
5579
+ if (isAnInterpreter(program)) return [innermost];
5580
+ if (!RUNNERS_OF_AN_INTERPRETER.has(basenameOf(program.text))) return [];
5581
+ const start = args.findIndex(isAnInterpreter);
5582
+ const interpreter = args[start];
5583
+ return interpreter === void 0 ? [] : [{ program: interpreter, args: args.slice(start + 1) }];
5584
+ };
5585
+ var inlineCodeOf = ({ args }) => args.flatMap((arg, index) => {
5586
+ const namesInlineCode = (FLAG.test(arg.text) || arg.text === INLINE_CODE_SUBCOMMAND) && arg.text !== STANDARD_INPUT_ARGUMENT;
5587
+ if (!namesInlineCode || !passesInlineCode(arg.text)) return [];
5588
+ const separator = arg.text.indexOf(FLAG_VALUE_SEPARATOR);
5589
+ if (separator !== -1) return [arg.text.slice(separator + 1)];
5590
+ const code = args[index + 1];
5591
+ return code === void 0 ? [] : [code.text];
5592
+ });
5593
+ var interpreterCodeIn = (command, invocations) => {
5594
+ const interpreters = invocations.flatMap(interpreterInvocationOf);
5595
+ const heredocs = interpreters.some(runsWhatItReads) ? withHeredocBodiesSetAside(command).bodies : [];
5596
+ return [...interpreters.flatMap(inlineCodeOf), ...heredocs];
5597
+ };
5598
+ 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, "permissions_off"));
5599
+ var runsInterpreterCodeThatNeedsAPerson = (command, invocations, depth) => interpreterCodeIn(command, invocations).some((code) => codeNeedsAPerson(code, depth));
5166
5600
  var WORDS_THAT_OPEN_A_COMMAND = String.raw`if|then|elif|else|do|while|until|!`;
5167
5601
  var SHELL_GROUPING_PREFIX = new RegExp(String.raw`^\s*(?:(?:${WORDS_THAT_OPEN_A_COMMAND})\s+|[({]\s*)+`);
5168
5602
  var GROUP_OPENER_BY_CLOSER = /* @__PURE__ */ new Map([[")", "("], ["}", "{"]]);
@@ -5201,7 +5635,7 @@ var withHeredocBodiesSetAside = (command) => {
5201
5635
  };
5202
5636
  var REDIRECTION_TARGET = String.raw`(?:'[^']*'|"[^"]*"|[^\s;|&()<>]+)`;
5203
5637
  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");
5638
+ var OUTPUT_REDIRECTION = new RegExp(String.raw`(?<!\d)(?:\d*|&)(?:>>?|>\||<>|>&|<&)\s*(${REDIRECTION_TARGET})`, "g");
5205
5639
  var REDIRECTED_INPUT_MARK = "\uE000";
5206
5640
  var ROUGH_SEPARATORS = ["&&", "||", "$(", "\n", ";", "|", "&", "`", "(", ")", "{", "}"];
5207
5641
  var SEPARATORS_INSIDE_DOUBLE_QUOTES = /* @__PURE__ */ new Set(["$(", "`", ")"]);
@@ -5243,6 +5677,10 @@ var invocationOfRoughPiece = (text3) => {
5243
5677
  const parsed = invocationOf(piece);
5244
5678
  return parsed.program !== void 0 ? parsed : invocationOf(piece.replace(QUOTE_CHARACTERS, ""));
5245
5679
  };
5680
+ var substitutionFedTo = (pieces, index) => {
5681
+ const next = pieces[index + 1];
5682
+ return next !== void 0 && SUBSTITUTION_OPENERS.has(next.before) ? next.text : null;
5683
+ };
5246
5684
  var roughSegmentsOf = (command) => {
5247
5685
  const { text: text3, bodies } = withHeredocBodiesSetAside(commandForReading(command));
5248
5686
  const pieces = roughPiecesOf(text3.replace(INPUT_REDIRECTION, ` ${REDIRECTED_INPUT_MARK} `).replace(OUTPUT_REDIRECTION, " "));
@@ -5251,48 +5689,108 @@ var roughSegmentsOf = (command) => {
5251
5689
  const heredocCount = occurrencesOf(piece.text, HEREDOC_MARK);
5252
5690
  const heredocs = bodies.slice(heredocsSeen, heredocsSeen + heredocCount);
5253
5691
  heredocsSeen += heredocCount;
5692
+ const substitution = substitutionFedTo(pieces, index);
5254
5693
  return {
5255
5694
  invocation: invocationOfRoughPiece(piece.text),
5256
5695
  readsInput: piece.text.includes(REDIRECTED_INPUT_MARK) || heredocCount > 0 || piece.before === PIPE3,
5257
5696
  readsAPipe: piece.before === PIPE3,
5258
- feedsASubstitution: SUBSTITUTION_OPENERS.has(pieces[index + 1]?.before ?? ""),
5697
+ feedsASubstitution: substitution !== null,
5698
+ substitution,
5699
+ evalRunsOneEnvironmentSetup: evalRunsOneEnvironmentSetup(pieces, index),
5259
5700
  heredocs
5260
5701
  };
5261
5702
  });
5262
5703
  };
5263
- var roughSegmentHasAVisibleConsequence = (toolName, segment, fetchesCode, depth) => {
5704
+ var ENVIRONMENT_SETUP_COMMANDS = [
5705
+ String.raw`fnm\s+env`,
5706
+ String.raw`brew\s+shellenv`,
5707
+ String.raw`(?:py|rb|nod|go|j)env\s+(?:virtualenv-)?init`,
5708
+ String.raw`direnv\s+(?:export|hook)`,
5709
+ "ssh-agent",
5710
+ String.raw`mise\s+(?:activate|env)`,
5711
+ String.raw`rtx\s+activate`,
5712
+ String.raw`zoxide\s+init`,
5713
+ String.raw`starship\s+init`,
5714
+ String.raw`conda\s+shell\.\w+\s+hook`,
5715
+ String.raw`minikube\s+docker-env`,
5716
+ String.raw`docker-machine\s+env`,
5717
+ String.raw`opam\s+env`,
5718
+ String.raw`aws\s+configure\s+export-credentials`,
5719
+ String.raw`poetry\s+env\s+activate`,
5720
+ "dircolors",
5721
+ String.raw`uv\s+generate-shell-completion`,
5722
+ String.raw`fzf\s+--(?:zsh|bash)`,
5723
+ String.raw`thefuck\s+--alias`,
5724
+ String.raw`luarocks\s+path`,
5725
+ String.raw`op\s+signin`,
5726
+ String.raw`(?:gh|kubectl|helm|npm|pnpm|rustup|deno|bun)\s+completion`,
5727
+ String.raw`(?:micro)?mamba\s+shell\s+hook`,
5728
+ "path_helper"
5729
+ ];
5730
+ var SETS_UP_THE_ENVIRONMENT = new RegExp(
5731
+ String.raw`^\s*(?:[^\s()$\x60"';|&]*/)?(?:${ENVIRONMENT_SETUP_COMMANDS.join("|")})(?:\s[^()$\x60"';|&]*)?$`
5732
+ );
5733
+ var EVAL_BEFORE_ITS_ARGUMENT = /^\s*eval\s+["']?$/;
5734
+ var EVAL_AFTER_ITS_ARGUMENT = /^["']?\s*$/;
5735
+ var SUBSTITUTION_CLOSERS = /* @__PURE__ */ new Set([")", "`"]);
5736
+ var WHAT_MAY_FOLLOW_AN_EVAL = /* @__PURE__ */ new Set(["&&", "||", ";", "\n", "|", "&", ")", "}"]);
5737
+ var evalRunsOneEnvironmentSetup = (pieces, index) => {
5738
+ const opening = pieces[index];
5739
+ const substitution = pieces[index + 1];
5740
+ const closing = pieces[index + 2];
5741
+ const next = pieces[index + 3];
5742
+ 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));
5743
+ };
5744
+ var evalRunsHiddenCode = (segment, innermost) => segment.substitution !== null && basenameOf(innermost.program?.text ?? "") === EVAL && !segment.evalRunsOneEnvironmentSetup;
5745
+ var roughSegmentHasAVisibleConsequence = (toolName, segment, producesHiddenCode, depth, line) => {
5264
5746
  const innermost = innermostInvocation(segment.invocation);
5265
5747
  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));
5748
+ return invocationHasAVisibleConsequence(toolName, segment.invocation, depth, line) || sqlClientSyntaxOf(innermost) !== void 0 && (segment.readsInput || segment.feedsASubstitution) || line === "permissions_off" && evalRunsHiddenCode(segment, innermost) || segment.readsAPipe && producesHiddenCode && runsWhatItReads(innermost) || runsAHeredocScript && segment.heredocs.some((body) => commandHasAVisibleConsequenceAt(toolName, body, depth + 1, line));
5267
5749
  };
5268
- var roughReadingHasAVisibleConsequence = (toolName, command, depth) => {
5750
+ var producesCodeTheLineCannotSee = (line) => line === "permissions_off" ? producesCodeTheCommandDoesNotShow : fetches;
5751
+ var roughReadingHasAVisibleConsequence = (toolName, command, depth, line) => {
5269
5752
  const segments = roughSegmentsOf(command);
5270
5753
  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);
5754
+ const producesHiddenCode = invocations.some(producesCodeTheLineCannotSee(line));
5755
+ return segments.some((segment) => roughSegmentHasAVisibleConsequence(toolName, segment, producesHiddenCode, depth, line)) || runsADownloadedScript(invocations) || line === "permissions_off" && runsInterpreterCodeThatNeedsAPerson(command, invocations, depth);
5273
5756
  };
5274
- var commandHasAVisibleConsequenceAt = (toolName, command, depth) => {
5757
+ var commandHidesWhatItWritesOrRuns = (command, readable) => RUNS_DECODED_CODE.test(readable) || redirectsIntoAFileThatNeedsAPerson(command);
5758
+ var commandHasAVisibleConsequenceAt = (toolName, command, depth, line) => {
5275
5759
  if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
5276
5760
  const readable = withHeredocSubstitutionsAsQuotedText(command);
5277
5761
  if (classifyDecisionRisk(toolName, readable) === "destructive" || RUNS_FETCHED_CODE.test(readable) || REDIRECTS_INTO_A_SECRET_FILE.test(readable)) return true;
5762
+ if (line === "permissions_off" && commandHidesWhatItWritesOrRuns(command, readable)) return true;
5278
5763
  const segments = readableCommandSegments(command);
5279
- if (segments === null) return roughReadingHasAVisibleConsequence(toolName, command, depth);
5764
+ if (segments === null) return roughReadingHasAVisibleConsequence(toolName, command, depth, line);
5280
5765
  const invocations = segments.map((segment) => invocationOf(withoutShellGrouping(segment)));
5281
- return invocations.some((invocation) => invocationHasAVisibleConsequence(toolName, invocation, depth)) || pipesIntoASqlClient(command) || pipesFetchedCodeIntoAnInterpreter(command, invocations) || runsADownloadedScript(invocations);
5766
+ return invocations.some((invocation) => invocationHasAVisibleConsequence(toolName, invocation, depth, line)) || pipesIntoASqlClient(command) || pipesCodeIntoAnInterpreter(command, invocations, producesCodeTheLineCannotSee(line)) || runsADownloadedScript(invocations) || line === "permissions_off" && runsInterpreterCodeThatNeedsAPerson(command, invocations, depth);
5767
+ };
5768
+ var REDIRECTION_OPERATOR_CHARACTER = /[<>]/;
5769
+ var redirectsIntoAFileThatNeedsAPerson = (command) => {
5770
+ if (!REDIRECTION_OPERATOR_CHARACTER.test(command)) return false;
5771
+ const text3 = withHeredocBodiesSetAside(commandForReading(command)).text;
5772
+ const visible = shellVisibleText(text3);
5773
+ return [...text3.matchAll(OUTPUT_REDIRECTION)].some((redirection) => {
5774
+ const operator = (redirection.index ?? 0) + redirection[0].search(REDIRECTION_OPERATOR_CHARACTER);
5775
+ return (visible === null || REDIRECTION_OPERATOR_CHARACTER.test(visible.charAt(operator))) && pathNeedsAPersonToChange((redirection[1] ?? "").replace(QUOTE_CHARACTERS, ""));
5776
+ });
5282
5777
  };
5283
5778
  var posixCommandOf = (words) => words.map(asOneShellWord).join(" ");
5284
- var posixShellJudgeFor = (toolName) => ({
5779
+ var namesASecretPowerShellPath = (path) => SECRET_FILE_NAME.test(fileNameOf(path));
5780
+ var posixShellJudgeFor = (toolName, line = "standing_allow") => ({
5285
5781
  namesADestructiveAction: (text3) => classifyDecisionRisk(toolName, text3) === "destructive",
5286
- invocationShowsAConsequence: (invocation) => invocationHasAVisibleConsequence(SHELL_TOOL, invocation, 0),
5287
- commandShowsAConsequence: (words) => commandHasAVisibleConsequenceAt(SHELL_TOOL, posixCommandOf(words), 1),
5782
+ invocationShowsAConsequence: (invocation) => invocationHasAVisibleConsequence(SHELL_TOOL, invocation, 0, line),
5783
+ commandShowsAConsequence: (words) => commandHasAVisibleConsequenceAt(SHELL_TOOL, posixCommandOf(words), 1, line),
5288
5784
  onlyReads: (words) => isSafeReadOnlyCommand(posixCommandOf(words)),
5289
5785
  fetches,
5290
5786
  runsWhatItReads,
5291
5787
  runsScripts,
5292
- namesASecretFile: (fileName) => SECRET_FILE_NAME.test(fileName)
5788
+ namesAFileThatNeedsAPerson: line === "permissions_off" ? pathNeedsAPersonToChange : namesASecretPowerShellPath
5293
5789
  });
5294
5790
  var commandIsDestructive = (toolName, command) => shellDialectOf(toolName) === "powershell" ? powerShellCommandIsDestructive(command, posixShellJudgeFor(toolName)) : readingsThatCanAct(command).some((reading) => classifyDecisionRisk(toolNameForAPosixReading(toolName), reading) === "destructive");
5295
- var commandHasAVisibleConsequence = (toolName, command) => shellDialectOf(toolName) === "powershell" ? powerShellCommandHasAVisibleConsequence(command, posixShellJudgeFor(toolName)) : commandHasAVisibleConsequenceAt(toolNameForAPosixReading(toolName), command, 0);
5791
+ var commandHasAConsequenceOnTheLine = (toolName, command, line) => shellDialectOf(toolName) === "powershell" ? powerShellCommandHasAVisibleConsequence(command, posixShellJudgeFor(toolName, line)) : commandHasAVisibleConsequenceAt(toolNameForAPosixReading(toolName), command, 0, line);
5792
+ var commandHasAVisibleConsequence = (toolName, command) => commandHasAConsequenceOnTheLine(toolName, command, "standing_allow");
5793
+ var commandHasAConsequenceWithPermissionsOff = (toolName, command) => commandHasAConsequenceOnTheLine(toolName, command, "permissions_off");
5296
5794
  var MAX_SPELLING_DEPTH = 3;
5297
5795
  var GIT_GLOBAL_OPTIONS_WITH_A_VALUE = /* @__PURE__ */ new Set([
5298
5796
  "-C",
@@ -6569,6 +7067,12 @@ var strictestScopeVerdict = (scope, paths) => {
6569
7067
  };
6570
7068
  var survivesDegraded = (config, toolName, toolInputs, cwd, repoKey, sessionId) => toolInputs.length === 1 && resolveAutoResolveOrigin(config, toolName, toolInputs[0], cwd, repoKey, sessionId) === "safe_readonly";
6571
7069
  var FULL_DEFER_MODES = /* @__PURE__ */ new Set(["plan", "auto", "dontAsk"]);
7070
+ var BYPASS_PERMISSION_MODE = "bypassPermissions";
7071
+ var BYPASS_STOPS = ["consequences", "nothing"];
7072
+ var DEFAULT_BYPASS_STOPS = "consequences";
7073
+ var isBypassStops = (value) => typeof value === "string" && BYPASS_STOPS.includes(value);
7074
+ var bypassStopsOf = (config) => isBypassStops(config.bypassStops) ? config.bypassStops : DEFAULT_BYPASS_STOPS;
7075
+ var isBypassPermissionMode = (permissionMode) => permissionMode === BYPASS_PERMISSION_MODE;
6572
7076
  var FILE_EDIT_TOOLS = /* @__PURE__ */ new Set([
6573
7077
  "Write",
6574
7078
  "Edit",
@@ -6580,7 +7084,7 @@ var shouldDeferToNativeMode = (permissionMode, toolName, options = {}) => {
6580
7084
  if (typeof permissionMode !== "string") return false;
6581
7085
  if (FULL_DEFER_MODES.has(permissionMode)) return true;
6582
7086
  if (permissionMode === "acceptEdits" && FILE_EDIT_TOOLS.has(toolName)) return true;
6583
- if (permissionMode === "bypassPermissions") return options.deferBypass === true;
7087
+ if (isBypassPermissionMode(permissionMode)) return options.deferBypass === true;
6584
7088
  return false;
6585
7089
  };
6586
7090
  var AGENT_QUESTION_TOOL = "AskUserQuestion";
@@ -6660,6 +7164,22 @@ var PRETOOLUSE_GATED_TOOLS = [
6660
7164
  var PRETOOLUSE_HANDLED_TOOLS = /* @__PURE__ */ new Set([...PRETOOLUSE_GATED_TOOLS, ...ASKED_IN_EVERY_MODE_TOOLS]);
6661
7165
  var isPermissionRequestOwnedByPreToolUse = (toolName, permissionMode, defers = shouldDeferToNativeMode) => PRETOOLUSE_HANDLED_TOOLS.has(toolName) && (isAskedInEveryMode(toolName) || !defers(permissionMode, toolName));
6662
7166
  var CLAUDE_PRETOOLUSE_MATCHER = [...PRETOOLUSE_HANDLED_TOOLS].join("|");
7167
+ var LINE_BREAK = /[\r\n]/;
7168
+ var bypassModeCovers = (toolName, toolInput) => {
7169
+ const arg = extractPolicyArg(toolName, toolInput);
7170
+ if (SHELL_DIALECT_BY_TOOL.has(toolName)) {
7171
+ return arg !== void 0 && !commandIsDestructive(toolName, arg) && !commandHasAConsequenceWithPermissionsOff(toolName, arg);
7172
+ }
7173
+ if (FILE_EDIT_TOOLS.has(toolName)) return arg !== void 0 && !LINE_BREAK.test(arg) && !fileEditNeedsAPersonWithPermissionsOff(toolName, arg);
7174
+ return mcpToolOnlyReads(toolName) && mcpToolInputOnlyReads(toolName, toolInput);
7175
+ };
7176
+ var aChosenRuleGovernsInEitherRepository = (input, counts = everyRule) => aChosenRuleGoverns(input, counts) || input.sharedRepoKey !== void 0 && input.sharedRepoKey !== input.repoKey && aChosenRuleGoverns({ ...input, repoKey: input.sharedRepoKey }, counts);
7177
+ var ruleAsksOrRefuses = (rule) => !isAutoApprove(rule);
7178
+ var bypassModeCoversEveryInput = (input) => input.deletesFiles !== true && input.toolInputs.every((toolInput) => bypassModeCovers(input.toolName, toolInput)) && !aChosenRuleGovernsInEitherRepository(input);
7179
+ var bypassModeApplies = (input) => isBypassPermissionMode(input.permissionMode) && !isModeStateDegraded(input.modeState) && noModeOverrideIsActive(input) && input.toolInputs.length > 0;
7180
+ var bypassModeAllows = (input) => bypassModeApplies(input) && (bypassStopsOf(input.config) === "nothing" ? !aChosenRuleGovernsInEitherRepository(input, ruleAsksOrRefuses) : bypassModeCoversEveryInput(input));
7181
+ var PERMISSIONS_OFF_ASK_REASON = "Permissions are off in your agent, so Pushary only stops for risky steps like this one. You can change this in Settings on pushary.com.";
7182
+ var permissionsOffAskReason = (input, verdict) => verdict.kind === "gate" && !verdict.scopeReason && !isAskedInEveryMode(input.toolName) && bypassModeApplies(input) && bypassStopsOf(input.config) === "consequences" && !aChosenRuleGovernsInEitherRepository(input) ? PERMISSIONS_OFF_ASK_REASON : void 0;
6663
7183
  var resolveGate = (input) => {
6664
7184
  const { modeState, config, toolName, toolInputs, cwd, sessionId } = input;
6665
7185
  const repoKey = input.sharedRepoKey ?? input.repoKey;
@@ -6677,8 +7197,9 @@ var resolveGate = (input) => {
6677
7197
  }
6678
7198
  const policy = resolvePolicyAcross(config, toolName, modeState.mode, toolInputs, cwd, repoKey, sessionId);
6679
7199
  const verdict = verdictForPolicy(policy);
6680
- const autoApproves = verdict === "allow";
6681
7200
  const autoDenies = verdict === "deny";
7201
+ const allowedByBypassMode = !autoDenies && verdict !== "allow" && bypassModeAllows(input);
7202
+ const autoApproves = verdict === "allow" || allowedByBypassMode;
6682
7203
  if (autoDenies) return { kind: "deny", policy, reason: `Denied by policy for ${policy.tool}` };
6683
7204
  if (isAskedInEveryMode(toolName)) return { kind: "gate", policy };
6684
7205
  if (isModeStateDegraded(modeState)) {
@@ -6692,7 +7213,7 @@ var resolveGate = (input) => {
6692
7213
  if (!scopeRequiresHuman(scopeVerdict)) {
6693
7214
  return {
6694
7215
  kind: "allow",
6695
- policy,
7216
+ policy: allowedByBypassMode ? autoApprove(policy.tool) : policy,
6696
7217
  source: toolInputs.length === 1 && resolveAutoResolveOrigin(config, toolName, toolInputs[0], cwd, repoKey, sessionId) === "safe_readonly" ? "allowlist" : "policy"
6697
7218
  };
6698
7219
  }
@@ -6718,7 +7239,7 @@ var SCOPE_BATCH_REASON = "This patch exceeds the agreed scope. Split it into sin
6718
7239
  var UNREADABLE_CALL_REASON = "Pushary could not read every target of this call. Use a supported command or a single-file edit.";
6719
7240
  var unapprovedScopeReason = (scopeReason) => `${scopeReason}. Nobody approved this path, so the change was refused.`;
6720
7241
  var NO_RESPONSE_REASON = "No response within timeout";
6721
- var codexRaisesItsOwnPrompts = (permissionMode) => permissionMode !== "bypassPermissions";
7242
+ var codexRaisesItsOwnPrompts = (permissionMode) => !isBypassPermissionMode(permissionMode);
6722
7243
  var codexHasNoPromptOfItsOwn = (scopeReason, permissionMode) => scopeReason !== void 0 || !codexRaisesItsOwnPrompts(permissionMode);
6723
7244
  var CODEX_ASK_PASSES = { kind: "pass" };
6724
7245
  var codexAskUnanswered = (ask) => ({ kind: "deny", reason: unansweredRefusalReason(ask.paused) });
@@ -6743,9 +7264,9 @@ var runsACommandThatNeedsAPerson = ({ toolName, toolInputs }) => SHELL_DIALECT_B
6743
7264
  const command = extractPolicyArg(toolName, toolInput);
6744
7265
  return command !== void 0 && (commandNeedsAPerson(toolName, command) || commandWritesThroughARemoteApi(command));
6745
7266
  });
6746
- var CI_WORKFLOW_FILE = /(?:^|\/)\.github\/workflows\/[^/]+$/i;
6747
7267
  var namesASecretOrWorkflowFile = (path) => SECRET_FILE_NAME.test(basenameOf(path)) || CI_WORKFLOW_FILE.test(normalizeSeparators(path));
6748
7268
  var fileEditNeedsAPerson = (toolName, path) => FILE_EDIT_TOOLS.has(toolName) && namesASecretOrWorkflowFile(path);
7269
+ var fileEditNeedsAPersonWithPermissionsOff = (toolName, path) => FILE_EDIT_TOOLS.has(toolName) && pathNeedsAPersonToChange(path);
6749
7270
  var editsAFileThatNeedsAPerson = ({ toolName, toolInputs }) => toolInputs.some((toolInput) => {
6750
7271
  const path = extractPolicyArg(toolName, toolInput);
6751
7272
  return path !== void 0 && fileEditNeedsAPerson(toolName, path);
@@ -6814,14 +7335,16 @@ var approvalRoutesOf = (config) => {
6814
7335
  const routes = config.approvalRoutes ?? [];
6815
7336
  return Array.isArray(routes) && routes.every(isApprovalRoutePattern) ? routes : null;
6816
7337
  };
6817
- var aChosenRuleGoverns = (input) => {
7338
+ var everyRule = () => true;
7339
+ var aChosenRuleGoverns = (input, counts = everyRule) => {
6818
7340
  const chosen = input.config.policies.filter((policy) => !isDefaultRule(policy));
6819
7341
  const routes = approvalRoutesOf(input.config);
6820
7342
  if (routes === null) return true;
6821
7343
  return input.toolInputs.some((toolInput) => {
6822
7344
  const args = policyArgForms(input.toolName, toolInput, input.cwd);
6823
7345
  const reading = policyReadingOf(chosen, input.toolName, args);
6824
- return governingRuleForReading(chosen, input.toolName, args, reading, input.repoKey, input.sessionId) !== void 0 || findBestRoute(routes, input.toolName, args, input.repoKey) !== void 0;
7346
+ const governing = governingRuleForReading(chosen, input.toolName, args, reading, input.repoKey, input.sessionId);
7347
+ return governing !== void 0 && counts(governing.policy) || findBestRoute(routes, input.toolName, args, input.repoKey) !== void 0;
6825
7348
  });
6826
7349
  };
6827
7350
  var claudeAllowlistOwnsCall = (input, verdict, permissionMode, rules) => rules !== null && verdict.kind === "gate" && !verdict.scopeReason && !aChosenRuleGoverns(input) && !isAskedInEveryMode(input.toolName) && claudeConsultsItsAllowlist(permissionMode) && agentRulesAllowEveryInput(rules, input.toolName, input.toolInputs, input.cwd);
@@ -7360,6 +7883,7 @@ export {
7360
7883
  PRETOOLUSE_HANDLED_TOOLS,
7361
7884
  isPermissionRequestOwnedByPreToolUse,
7362
7885
  CLAUDE_PRETOOLUSE_MATCHER,
7886
+ permissionsOffAskReason,
7363
7887
  resolveGate,
7364
7888
  editGoesToTheAgentsPrompt,
7365
7889
  SCOPE_BATCH_REASON,