pushary 1.9.24 → 1.9.25

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 (100) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/bin/pushary-bell-hook.js +25 -25
  3. package/dist/bin/pushary-bell.js +9 -9
  4. package/dist/bin/pushary-claude.js +24 -24
  5. package/dist/bin/pushary-clean.js +29 -29
  6. package/dist/bin/pushary-codex-bridge.js +17 -17
  7. package/dist/bin/pushary-codex-hook.js +30 -30
  8. package/dist/bin/pushary-codex.js +12 -12
  9. package/dist/bin/pushary-connect.js +24 -24
  10. package/dist/bin/pushary-cowork.js +5 -5
  11. package/dist/bin/pushary-daemon.js +31 -31
  12. package/dist/bin/pushary-disconnect.js +24 -24
  13. package/dist/bin/pushary-doctor.js +33 -33
  14. package/dist/bin/pushary-elicitation-hook.js +25 -25
  15. package/dist/bin/pushary-gemini-bridge.js +13 -13
  16. package/dist/bin/pushary-gemini-hook.js +27 -27
  17. package/dist/bin/pushary-hook.js +29 -29
  18. package/dist/bin/pushary-login.js +12 -12
  19. package/dist/bin/pushary-logout.js +24 -24
  20. package/dist/bin/pushary-mcp.js +9 -9
  21. package/dist/bin/pushary-mode.js +12 -12
  22. package/dist/bin/pushary-notification-hook.js +24 -24
  23. package/dist/bin/pushary-opencode-hook.js +26 -26
  24. package/dist/bin/pushary-permission-denied-hook.js +29 -29
  25. package/dist/bin/pushary-permission-hook.js +28 -28
  26. package/dist/bin/pushary-post-hook.js +24 -24
  27. package/dist/bin/pushary-prompt-hook.js +24 -24
  28. package/dist/bin/pushary-session-end-hook.js +24 -24
  29. package/dist/bin/pushary-session-start-hook.js +28 -28
  30. package/dist/bin/pushary-setup.js +35 -35
  31. package/dist/bin/pushary-stats.js +5 -5
  32. package/dist/bin/pushary-status.js +26 -26
  33. package/dist/bin/pushary-stop-hook.js +24 -24
  34. package/dist/bin/pushary-stopfailure-hook.js +24 -24
  35. package/dist/bin/pushary-suggestions.js +12 -12
  36. package/dist/bin/pushary-transcript-register.js +17 -17
  37. package/dist/bin/pushary-transcripts.js +7 -7
  38. package/dist/bin/pushary-upgrade.js +30 -30
  39. package/dist/bin/pushary-wait.js +12 -12
  40. package/dist/bin/pushary.js +6 -6
  41. package/dist/{chunk-GBD6G4TA.js → chunk-2CUE3XXS.js} +1 -1
  42. package/dist/{chunk-T5KM7J75.js → chunk-2J2WFX7X.js} +4 -4
  43. package/dist/{chunk-NQICO3ES.js → chunk-2VQZC6DW.js} +4 -4
  44. package/dist/{chunk-G6GFNKF7.js → chunk-2XHBYMVN.js} +11 -11
  45. package/dist/{chunk-7JHYWFY6.js → chunk-3UYMOWX6.js} +2 -2
  46. package/dist/{chunk-NJK4MGXL.js → chunk-4KZP4YMV.js} +1 -1
  47. package/dist/{chunk-T6TJA4RF.js → chunk-5ETE7F45.js} +2 -2
  48. package/dist/{chunk-OY6NJWDD.js → chunk-5Q2QJ2AL.js} +1 -1
  49. package/dist/{chunk-LOERK6N3.js → chunk-5WTEKIAS.js} +4 -4
  50. package/dist/{chunk-W5NHIA4Y.js → chunk-7UZD2F4Y.js} +8 -8
  51. package/dist/{chunk-RK7TDMQT.js → chunk-7V6PJFDF.js} +3 -3
  52. package/dist/{chunk-RFK2WN6S.js → chunk-A3QQWXIE.js} +1 -1
  53. package/dist/{chunk-KT5BH6GC.js → chunk-AXB4ABUD.js} +8 -8
  54. package/dist/{chunk-EKPAV33G.js → chunk-BGJDOHNF.js} +5 -5
  55. package/dist/{chunk-CLVU24OZ.js → chunk-BHRMHDOO.js} +2 -2
  56. package/dist/{chunk-Q7IEFI7V.js → chunk-EJJNZYDS.js} +2 -2
  57. package/dist/{chunk-GXQVOKHJ.js → chunk-EJMXZH5E.js} +2 -2
  58. package/dist/{chunk-RIBIEQHU.js → chunk-FCXYDR62.js} +1 -1
  59. package/dist/{chunk-O3RRQTTV.js → chunk-FJKV64M7.js} +5 -5
  60. package/dist/{chunk-3WRJU7KN.js → chunk-FLC4TVXH.js} +1 -1
  61. package/dist/{chunk-7Z2DLAEE.js → chunk-G446AVR5.js} +2 -2
  62. package/dist/{chunk-U23XTMBF.js → chunk-GC5HJE3T.js} +3 -3
  63. package/dist/{chunk-35U2QHH2.js → chunk-HEJMZVJI.js} +2 -2
  64. package/dist/{chunk-GM2IFDVF.js → chunk-KGSN2SOL.js} +769 -17
  65. package/dist/{chunk-VFIZFKZY.js → chunk-KM7AEGKF.js} +3 -3
  66. package/dist/{chunk-264EOGIK.js → chunk-KP4BIZLP.js} +2 -2
  67. package/dist/{chunk-XA2RGGNR.js → chunk-KZLOVLA6.js} +4 -4
  68. package/dist/{chunk-SH2KK2ED.js → chunk-LDNKGG5G.js} +3 -3
  69. package/dist/{chunk-SBQCRQIP.js → chunk-N3ACK3YA.js} +4 -4
  70. package/dist/{chunk-ZVON5PCF.js → chunk-NFH2H7PD.js} +5 -5
  71. package/dist/{chunk-URCSJZNU.js → chunk-OFHSTCXK.js} +3 -3
  72. package/dist/{chunk-QDKNLNCA.js → chunk-OOKIWOMK.js} +1 -1
  73. package/dist/{chunk-KX776UXN.js → chunk-OWOSNGEQ.js} +2 -2
  74. package/dist/{chunk-HBR6ZZHN.js → chunk-PAMW5M53.js} +5 -5
  75. package/dist/{chunk-Z5L4NQHC.js → chunk-PM2WJSUH.js} +1 -1
  76. package/dist/{chunk-WYLA6J43.js → chunk-PPGLIWOR.js} +1 -1
  77. package/dist/{chunk-OLWKCDK4.js → chunk-PSNF27BC.js} +6 -6
  78. package/dist/{chunk-HLXGEOX3.js → chunk-Q37XFSPO.js} +4 -4
  79. package/dist/{chunk-EE357RIP.js → chunk-RSENVGBQ.js} +4 -4
  80. package/dist/{chunk-E4B7TXVS.js → chunk-SGGR5MN2.js} +1 -1
  81. package/dist/{chunk-P447F2RB.js → chunk-TLUSUDRU.js} +1 -1
  82. package/dist/{chunk-L5MCJNSI.js → chunk-UJB6HHP4.js} +5 -5
  83. package/dist/{chunk-2F6UQ3YD.js → chunk-UK7OY3EJ.js} +1 -1
  84. package/dist/{chunk-IO5ZDIHY.js → chunk-UUOC7A7M.js} +7 -7
  85. package/dist/{chunk-H2RD5EMR.js → chunk-VEGS4NYF.js} +3 -3
  86. package/dist/{chunk-XQROISQB.js → chunk-VFF7ZBIS.js} +2 -2
  87. package/dist/{chunk-LHFK5F3T.js → chunk-VN5Q4KOD.js} +2 -2
  88. package/dist/{chunk-2JTJZ7VI.js → chunk-WEKXYS2F.js} +4 -4
  89. package/dist/{chunk-CYGIJXAP.js → chunk-WKEP4JAW.js} +2 -2
  90. package/dist/{chunk-JDWOK6PE.js → chunk-YD3DIFIU.js} +1 -1
  91. package/dist/{chunk-IS737HST.js → chunk-ZLGCNHJG.js} +1 -1
  92. package/dist/{chunk-OSOCONIE.js → chunk-ZLY7BHN4.js} +5 -5
  93. package/dist/{chunk-VICG46JC.js → chunk-ZNT4ZZXJ.js} +1 -1
  94. package/dist/{chunk-2E6AWHV7.js → chunk-ZV3262AA.js} +3 -3
  95. package/dist/{global-upgrade-DZGLAARJ.js → global-upgrade-4M4TJKQR.js} +6 -6
  96. package/dist/{paths-JATF66FP.js → paths-6JY6J56N.js} +2 -2
  97. package/dist/{reapply-7TDCFXNF.js → reapply-LFEQ5GF6.js} +20 -20
  98. package/dist/src/index.js +19 -19
  99. package/dist/{upgrade-lock-LPIDFQEH.js → upgrade-lock-CZRCHYVU.js} +3 -3
  100. package/package.json +1 -1
@@ -708,12 +708,14 @@ var scanShell = (command) => {
708
708
  }
709
709
  return quote === null ? lexemes : null;
710
710
  };
711
- var shellCommandSegments = (command) => {
711
+ var PIPE = "|";
712
+ var splitShellSegments = (command) => {
712
713
  const lexemes = scanShell(command);
713
714
  if (lexemes === null) return null;
714
715
  const segments = [];
715
716
  let start = 0;
716
717
  let consumed = -1;
718
+ let readsPipe = false;
717
719
  for (const lexeme of lexemes) {
718
720
  const { index, text, quote, kind } = lexeme;
719
721
  if (index <= consumed || kind !== "text" || quote === "'") continue;
@@ -721,14 +723,103 @@ var shellCommandSegments = (command) => {
721
723
  if (quote !== null) continue;
722
724
  if (UNDECOMPOSABLE.has(text)) return null;
723
725
  if (!SEGMENT_SEPARATORS.has(text)) continue;
724
- consumed = index + (DOUBLED_SEPARATORS.has(text) && command[index + 1] === text ? 1 : 0);
725
- segments.push(trimShellWhitespace(command.slice(start, index)));
726
+ const doubled = DOUBLED_SEPARATORS.has(text) && command[index + 1] === text;
727
+ consumed = index + (doubled ? 1 : 0);
728
+ segments.push({ text: trimShellWhitespace(command.slice(start, index)), readsPipe });
729
+ readsPipe = text === PIPE && !doubled;
726
730
  start = consumed + 1;
727
731
  }
728
- segments.push(trimShellWhitespace(command.slice(start)));
729
- const present = segments.filter((segment) => segment.length > 0);
732
+ segments.push({ text: trimShellWhitespace(command.slice(start)), readsPipe });
733
+ const present = segments.filter((segment) => segment.text.length > 0);
730
734
  return present.length > 0 ? present : null;
731
735
  };
736
+ var shellCommandSegments = (command) => splitShellSegments(command)?.map((segment) => segment.text) ?? null;
737
+ var shellSegmentsReadingAPipe = (command) => splitShellSegments(command)?.filter((segment) => segment.readsPipe).map((segment) => segment.text) ?? null;
738
+ var SUBSTITUTION_OPEN = "$(";
739
+ var QUOTED_HEREDOC_CAT = /^[ \t]*cat[ \t]+<<[ \t]*(?:'([A-Za-z_][A-Za-z0-9_]*)'|"([A-Za-z_][A-Za-z0-9_]*)")[ \t]*\n/;
740
+ var CLOSING_PARENTHESIS = /^\s*\)/;
741
+ var LEADING_TABS = /^\t+/;
742
+ var CHARACTERS_ESCAPED_IN_DOUBLE_QUOTES = /["\\$`]/g;
743
+ var isHeredocTerminator = (line, delimiter) => line.replace(LEADING_TABS, "").trimEnd() === delimiter;
744
+ var mightEndAHeredoc = (line, delimiter) => line.trimStart().startsWith(delimiter);
745
+ var BODY_ESCAPES = /[\\`]/;
746
+ var COMMENT_START = "#";
747
+ var WHITESPACE = /\s/;
748
+ var parenthesesBalanceInBody = (body, readsComments) => {
749
+ let depth = 0;
750
+ let quote = null;
751
+ let comment = false;
752
+ let wordStart = true;
753
+ for (const char of body) {
754
+ if (comment) {
755
+ comment = char !== "\n";
756
+ wordStart = true;
757
+ continue;
758
+ }
759
+ if (quote !== null) {
760
+ if (char === quote) quote = null;
761
+ wordStart = false;
762
+ continue;
763
+ }
764
+ if (readsComments && wordStart && char === COMMENT_START) {
765
+ comment = true;
766
+ continue;
767
+ }
768
+ if (char === "'" || char === '"') quote = char;
769
+ if (char === "(") depth += 1;
770
+ if (char === ")") depth -= 1;
771
+ if (depth < 0) return false;
772
+ wordStart = WHITESPACE.test(char);
773
+ }
774
+ return depth === 0 && quote === null;
775
+ };
776
+ var everyBashReadsTheBodyAsText = (body) => !BODY_ESCAPES.test(body) && parenthesesBalanceInBody(body, true) && parenthesesBalanceInBody(body, false);
777
+ var quotedHeredocSubstitutionAt = (command, open) => {
778
+ const header = QUOTED_HEREDOC_CAT.exec(command.slice(open + SUBSTITUTION_OPEN.length));
779
+ const delimiter = header?.[1] ?? header?.[2];
780
+ if (!header || !delimiter) return null;
781
+ const bodyStart = open + SUBSTITUTION_OPEN.length + header[0].length;
782
+ for (let lineStart = bodyStart; lineStart <= command.length; ) {
783
+ const newline = command.indexOf("\n", lineStart);
784
+ const lineEnd = newline === -1 ? command.length : newline;
785
+ const line = command.slice(lineStart, lineEnd);
786
+ if (line === delimiter) {
787
+ const closing = CLOSING_PARENTHESIS.exec(command.slice(lineEnd));
788
+ const body = command.slice(bodyStart, Math.max(bodyStart, lineStart - 1));
789
+ return closing && everyBashReadsTheBodyAsText(body) ? { body, end: lineEnd + closing[0].length } : null;
790
+ }
791
+ if (mightEndAHeredoc(line, delimiter)) return null;
792
+ if (newline === -1) return null;
793
+ lineStart = newline + 1;
794
+ }
795
+ return null;
796
+ };
797
+ var asDoubleQuotedText = (body) => body.replace(CHARACTERS_ESCAPED_IN_DOUBLE_QUOTES, (character) => `\\${character}`);
798
+ var withHeredocSubstitutionsAsQuotedText = (command) => {
799
+ let result = "";
800
+ let copied = 0;
801
+ let quote = null;
802
+ for (let index = 0; index < command.length; index += 1) {
803
+ const char = command.charAt(index);
804
+ if (char === "\\" && quote !== "'") {
805
+ index += 1;
806
+ continue;
807
+ }
808
+ if ((char === "'" || char === '"') && (quote === null || quote === char)) {
809
+ quote = quote === null ? char : null;
810
+ continue;
811
+ }
812
+ if (quote === "'") continue;
813
+ if (char === "`") break;
814
+ if (char !== "$" || command.charAt(index + 1) !== "(") continue;
815
+ const substitution = quote === '"' ? quotedHeredocSubstitutionAt(command, index) : null;
816
+ if (substitution === null) break;
817
+ result += command.slice(copied, index) + asDoubleQuotedText(substitution.body);
818
+ copied = substitution.end;
819
+ index = substitution.end - 1;
820
+ }
821
+ return result + command.slice(copied);
822
+ };
732
823
  var shellVisibleText = (command) => {
733
824
  const lexemes = scanShell(command);
734
825
  if (lexemes === null) return null;
@@ -1183,11 +1274,12 @@ var PATTERN_INTENT = {
1183
1274
  grant: "grant",
1184
1275
  restrict: "restrict"
1185
1276
  };
1277
+ var segmentsForMatching = (command) => shellCommandSegments(withHeredocSubstitutionsAsQuotedText(command));
1186
1278
  var everySegmentMatches = (command, matches) => {
1187
- const segments = shellCommandSegments(command);
1279
+ const segments = segmentsForMatching(command);
1188
1280
  return segments !== null && segments.every(matches);
1189
1281
  };
1190
- var anySegmentMatches = (command, matches) => (shellCommandSegments(command) ?? [command]).some(matches);
1282
+ var anySegmentMatches = (command, matches) => (segmentsForMatching(command) ?? [command]).some(matches);
1191
1283
  var shellSegmentsMatch = (command, intent, matches) => intent === PATTERN_INTENT.restrict ? anySegmentMatches(command, matches) : everySegmentMatches(command, matches);
1192
1284
  var rankWholeOrSegments = (toolName, arg, intent, matches) => {
1193
1285
  const matched = toolName in POLICY_COMMAND_KEYS ? shellSegmentsMatch(arg, intent, matches) : matches(arg);
@@ -1646,7 +1738,9 @@ var INLINE_CODE_INTERPRETERS = /* @__PURE__ */ new Set([
1646
1738
  "lua",
1647
1739
  "osascript",
1648
1740
  "pwsh",
1649
- "powershell"
1741
+ "powershell",
1742
+ "mongosh",
1743
+ "mongo"
1650
1744
  ]);
1651
1745
  var INLINE_CODE_LONG_FLAG = /^-{1,2}(?:eval|print|require|import|command|encodedcommand|enc|ec)(?:=|$)/i;
1652
1746
  var INLINE_CODE_SHORT_FLAG_LETTERS = /* @__PURE__ */ new Set(["c", "e", "E", "p", "r"]);
@@ -1669,7 +1763,15 @@ var DESTRUCTIVE_ARGUMENTS_BY_COMMAND = /* @__PURE__ */ new Map([
1669
1763
  ])],
1670
1764
  ["git checkout", /* @__PURE__ */ new Set(["-f", "--force", "--", "-B"])],
1671
1765
  ["git switch", /* @__PURE__ */ new Set(["-f", "--force", "--discard-changes", "-C", "--force-create"])],
1672
- ["git branch", /* @__PURE__ */ new Set(["-f", "--force", "-D", "-M", "-C"])]
1766
+ ["git branch", /* @__PURE__ */ new Set(["-f", "--force", "-D", "-M", "-C"])],
1767
+ ["git push", /* @__PURE__ */ new Set(["-f", "--force", "--force-with-lease", "--force-if-includes", "--mirror", "-d", "--delete", "--prune"])],
1768
+ ["git clean", /* @__PURE__ */ new Set(["-f", "--force"])],
1769
+ ["rm", /* @__PURE__ */ new Set(["-r", "-R", "--recursive"])],
1770
+ ["pg_restore", /* @__PURE__ */ new Set(["-c", "--clean"])],
1771
+ ["mongorestore", /* @__PURE__ */ new Set(["--drop"])],
1772
+ ["aws s3", /* @__PURE__ */ new Set(["--delete"])],
1773
+ ["gsutil rsync", /* @__PURE__ */ new Set(["-d", "--delete-unmatched-destination-objects"])],
1774
+ ["gcloud storage", /* @__PURE__ */ new Set(["--delete-unmatched-destination-objects"])]
1673
1775
  ]);
1674
1776
  var DESTRUCTIVE_WORDS_IN_ANY_CASE_BY_COMMAND = /* @__PURE__ */ new Map([
1675
1777
  ["redis-cli", /* @__PURE__ */ new Set(["flushall", "flushdb"])]
@@ -1710,7 +1812,8 @@ var SUBCOMMAND_CLIS = /* @__PURE__ */ new Set([
1710
1812
  "heroku",
1711
1813
  "supabase",
1712
1814
  "wrangler",
1713
- "railway"
1815
+ "railway",
1816
+ "bq"
1714
1817
  ]);
1715
1818
  var DESTRUCTIVE_SUBCOMMANDS = /* @__PURE__ */ new Set([
1716
1819
  "delete",
@@ -1812,33 +1915,681 @@ var argumentsHideTheEffect = (name, args) => {
1812
1915
  return (meaningful[0]?.text.startsWith("-") ?? false) || subcommandWordsOf(meaningful).some((word) => word.expands || RUNNER_SUBCOMMANDS.has(word.text));
1813
1916
  };
1814
1917
  var invocationHidesItsEffect = ({ program, args }) => program !== void 0 && ([program, ...args].some((word) => word.comment) || programHidesItsEffect(program) || argumentsHideTheEffect(basenameOf(program.text), args));
1815
- var readableCommandSegments = (command) => shellCommandSegments(command.replace(SHELL_LINE_CONTINUATION, "").replace(SAFE_REDIRECTION, ""));
1816
- var normalizedInvocation = ({ program, args }) => {
1918
+ var commandForReading = (command) => withHeredocSubstitutionsAsQuotedText(command).replace(SHELL_LINE_CONTINUATION, "").replace(SAFE_REDIRECTION, "");
1919
+ var readableCommandSegments = (command) => shellCommandSegments(commandForReading(command));
1920
+ var invocationTextWith = ({ program, args }, wordText) => {
1817
1921
  if (program === void 0) return "";
1818
1922
  const name = basenameOf(program.text);
1819
1923
  const kept = SUBCOMMAND_CLIS.has(name) ? argsAfterHarmlessGlobalFlags(args) : args;
1820
- return [name, ...kept.map((arg) => arg.text)].join(" ");
1821
- };
1924
+ return [name, ...kept.map(wordText)].join(" ");
1925
+ };
1926
+ var normalizedInvocation = (invocation) => invocationTextWith(invocation, (word) => word.text);
1927
+ var SHELL_METACHARACTER = /[\s;&|<>()$`\\"'*?[\]#~{}!]/;
1928
+ var SINGLE_QUOTE = "'";
1929
+ var ESCAPED_SINGLE_QUOTE = `'\\''`;
1930
+ var asOneShellWord = (text) => SHELL_METACHARACTER.test(text) ? `${SINGLE_QUOTE}${text.replaceAll(SINGLE_QUOTE, ESCAPED_SINGLE_QUOTE)}${SINGLE_QUOTE}` : text;
1931
+ var quotedInvocation = (invocation) => invocationTextWith(invocation, (word) => asOneShellWord(word.text));
1822
1932
  var normalizedCommand = (command) => {
1823
1933
  const segments = readableCommandSegments(command);
1824
1934
  return segments === null ? null : segments.map((segment) => normalizedInvocation(invocationOf(segment))).join("; ");
1825
1935
  };
1826
1936
  var subcommandDestroys = ({ program, args }) => program !== void 0 && SUBCOMMAND_CLIS.has(basenameOf(program.text)) && subcommandWordsOf(argsAfterHarmlessGlobalFlags(args)).some((word) => subcommandWordDestroys(word.text));
1937
+ var wordsOfInvocation = ({ program, args }) => program === void 0 ? args : [program, ...args];
1938
+ var PRODUCTION_NAMES = /* @__PURE__ */ new Set(["prod", "production"]);
1939
+ var PRODUCTION_FLAGS = /* @__PURE__ */ new Set(["--prod", "--production"]);
1940
+ var ENVIRONMENT_FLAGS = /* @__PURE__ */ new Set([
1941
+ "-e",
1942
+ "--env",
1943
+ "--environment",
1944
+ "--stage",
1945
+ "--profile",
1946
+ "--context",
1947
+ "--target"
1948
+ ]);
1949
+ var ENVIRONMENT_VARIABLE = /^[A-Z_]*(?:ENV|ENVIRONMENT|STAGE)$/;
1950
+ var FLAG_VALUE_SEPARATOR = "=";
1951
+ var DEPLOY_CLIS = /* @__PURE__ */ new Set([
1952
+ "vercel",
1953
+ "netlify",
1954
+ "fly",
1955
+ "flyctl",
1956
+ "heroku",
1957
+ "railway",
1958
+ "firebase",
1959
+ "wrangler",
1960
+ "supabase",
1961
+ "serverless",
1962
+ "sls"
1963
+ ]);
1964
+ var SCHEMA_CHANGE_WORD = /(?:^|[\s:_./-])(?:migrat(?:e|es|ion|ions)|rollback|seed|db[:\s]push)(?=$|[\s:_./-])(?![\s:_./-]+(?:status|version|list|ls|show|check|diff|history|info)(?:$|[\s:_./-]))/i;
1965
+ var PRODUCTION_READ_VERBS = /* @__PURE__ */ new Set([
1966
+ "logs",
1967
+ "log",
1968
+ "tail",
1969
+ "status",
1970
+ "list",
1971
+ "ls",
1972
+ "info",
1973
+ "inspect",
1974
+ "describe",
1975
+ "get",
1976
+ "show",
1977
+ "whoami",
1978
+ "build",
1979
+ "dev",
1980
+ "open",
1981
+ "history",
1982
+ "metrics",
1983
+ "help",
1984
+ "version",
1985
+ "login",
1986
+ "link",
1987
+ "pull"
1988
+ ]);
1989
+ var PRODUCTION_SETTING_NAMESPACES = /* @__PURE__ */ new Set(["env", "secret", "secrets", "config", "domains", "dns", "certs"]);
1990
+ var deployCliOperandsAfter = (words, cli) => {
1991
+ const after = words.slice(cli + 1);
1992
+ const firstFlag = after.findIndex((word) => FLAG.test(word.text));
1993
+ return (firstFlag === -1 ? after : after.slice(0, firstFlag)).map((word) => word.text.toLowerCase());
1994
+ };
1995
+ var deployCliOnlyReads = (operands) => {
1996
+ const [first, second] = operands.map((operand) => operand.split(":")[0] ?? "");
1997
+ const verb = PRODUCTION_SETTING_NAMESPACES.has(first ?? "") ? second : first;
1998
+ return verb !== void 0 && PRODUCTION_READ_VERBS.has(verb);
1999
+ };
2000
+ var deployCliChangesSomething = (words) => words.some((word, index) => DEPLOY_CLIS.has(basenameOf(word.text)) && !deployCliOnlyReads(deployCliOperandsAfter(words, index)));
2001
+ var valueSelectsProduction = (name, value, names) => names.has(name) && PRODUCTION_NAMES.has(value.toLowerCase());
2002
+ var wordSelectsProduction = (word, previous) => {
2003
+ const separator = word.text.indexOf(FLAG_VALUE_SEPARATOR);
2004
+ const name = separator > 0 ? word.text.slice(0, separator) : "";
2005
+ const value = separator > 0 ? word.text.slice(separator + 1) : "";
2006
+ return PRODUCTION_FLAGS.has(word.text.toLowerCase()) || valueSelectsProduction(name.toLowerCase(), value, ENVIRONMENT_FLAGS) || ENVIRONMENT_VARIABLE.test(name) && PRODUCTION_NAMES.has(value.toLowerCase()) || valueSelectsProduction((previous?.text ?? "").toLowerCase(), word.text, ENVIRONMENT_FLAGS);
2007
+ };
2008
+ var changesProduction = (invocation) => {
2009
+ const words = wordsOfInvocation(invocation);
2010
+ return words.some((word, index) => wordSelectsProduction(word, words[index - 1])) && (deployCliChangesSomething(words) || SCHEMA_CHANGE_WORD.test(normalizedInvocation(invocation)));
2011
+ };
2012
+ var MYSQL_SYNTAX = { inlineFlags: /* @__PURE__ */ new Set(["-e", "--execute"]), scriptFlags: /* @__PURE__ */ new Set(), positionalSql: false };
2013
+ var SQLITE_SYNTAX = { inlineFlags: /* @__PURE__ */ new Set(["-cmd"]), scriptFlags: /* @__PURE__ */ new Set(["-init"]), positionalSql: true };
2014
+ var SQL_CLIENT_SYNTAX = /* @__PURE__ */ new Map([
2015
+ ["psql", { inlineFlags: /* @__PURE__ */ new Set(["-c", "--command"]), scriptFlags: /* @__PURE__ */ new Set(["-f", "--file"]), positionalSql: false }],
2016
+ ["mysql", MYSQL_SYNTAX],
2017
+ ["mariadb", MYSQL_SYNTAX],
2018
+ ["sqlite3", SQLITE_SYNTAX],
2019
+ ["sqlite", SQLITE_SYNTAX],
2020
+ ["duckdb", { inlineFlags: /* @__PURE__ */ new Set(["-c", "-s", "-cmd"]), scriptFlags: /* @__PURE__ */ new Set(["-f", "-init"]), positionalSql: true }],
2021
+ ["clickhouse-client", { inlineFlags: /* @__PURE__ */ new Set(["-q", "--query"]), scriptFlags: /* @__PURE__ */ new Set(["--queries-file"]), positionalSql: false }],
2022
+ ["bq", { inlineFlags: /* @__PURE__ */ new Set(), scriptFlags: /* @__PURE__ */ new Set(), positionalSql: true }],
2023
+ ["mongosh", { inlineFlags: /* @__PURE__ */ new Set(["--eval"]), scriptFlags: /* @__PURE__ */ new Set(["-f", "--file"]), positionalSql: false }],
2024
+ ["mongo", { inlineFlags: /* @__PURE__ */ new Set(["--eval"]), scriptFlags: /* @__PURE__ */ new Set(), positionalSql: false }]
2025
+ ]);
2026
+ var SQL_WRITE_STATEMENT = new RegExp([
2027
+ String.raw`\b(?:insert|update|delete|merge|upsert|alter|create|drop|truncate|grant|revoke|copy|vacuum|reindex|rename|attach|detach)\b`,
2028
+ String.raw`\breplace\s+into\b`,
2029
+ String.raw`\bload\s+data\b`,
2030
+ String.raw`\binto\s+(?:out|dump)file\b`,
2031
+ String.raw`\bset\s+(?:global|persist)\b`,
2032
+ String.raw`\bkill\s+(?:query|connection|\d)`,
2033
+ String.raw`\bcall\s+[\w.]+\s*\(`,
2034
+ String.raw`\bdo\s+(?:\$\w*\$|')`,
2035
+ String.raw`\bpg_terminate_backend\b`,
2036
+ String.raw`\bsetval\b`,
2037
+ String.raw`\bpragma\s+writable_schema\b`,
2038
+ String.raw`\.(?:insert|update|delete|replace|remove|drop|rename|create|bulkwrite|findoneand)\w*\s*\(`
2039
+ ].join("|"), "i");
2040
+ var SQL_READ_PHRASE = /\bshow\s+create\b/gi;
2041
+ var SQL_STRING_LITERAL = /'(?:[^']|'')*'/g;
2042
+ var EMPTY_SQL_STRING = "''";
2043
+ var SQL_COMMENT = /--[^\n]*|\/\*[\s\S]*?\*\//g;
2044
+ var writesThroughSql = (sql) => {
2045
+ const statement = sql.replace(SQL_COMMENT, " ").replace(SQL_STRING_LITERAL, EMPTY_SQL_STRING).replace(SQL_READ_PHRASE, "");
2046
+ return SQL_WRITE_STATEMENT.test(statement) || SQL_CLIENT_FILE_COMMAND.test(statement);
2047
+ };
2048
+ var SQL_CLIENT_FILE_COMMAND = new RegExp([
2049
+ String.raw`(?:^|[\s;=])\\(?:i|ir|include|include_relative|o|out|w|write|gexec|!|\.)(?=\s|$)`,
2050
+ String.raw`(?:^|[\s;=])\\g\s*\|`,
2051
+ String.raw`(?:^|[\s;=])\.(?:read|import|restore|load|output|once|shell|system|backup|save|open)(?=\s|$)`,
2052
+ String.raw`(?:^|[;=])\s*(?:system|source)\s`
2053
+ ].join("|"), "i");
2054
+ var SHORT_FLAG_CLUSTER_ONLY = /^-([A-Za-z0-9]+)$/;
2055
+ var NO_SQL = { script: false, sql: null, hidden: false };
2056
+ var shortFlagOf = (flags, text) => [...flags].find((flag) => /^-[A-Za-z]$/.test(flag) && text.startsWith(flag) && text.length > flag.length && !text.startsWith("--"));
2057
+ var sqlArgumentAt = (args, index, syntax) => {
2058
+ const arg = args[index];
2059
+ if (arg === void 0) return NO_SQL;
2060
+ const next = args[index + 1];
2061
+ const valueOf = (word) => ({ script: false, sql: word?.text ?? null, hidden: word?.expands ?? false });
2062
+ if (syntax.scriptFlags.has(arg.text) || namesALongFlagValue(arg.text, syntax.scriptFlags) || shortFlagOf(syntax.scriptFlags, arg.text)) {
2063
+ return { script: true, sql: null, hidden: false };
2064
+ }
2065
+ if (syntax.inlineFlags.has(arg.text)) return valueOf(next);
2066
+ const longInline = [...syntax.inlineFlags].find((flag) => arg.text.startsWith(`${flag}${FLAG_VALUE_SEPARATOR}`));
2067
+ if (longInline) return { script: false, sql: arg.text.slice(longInline.length + 1), hidden: arg.expands };
2068
+ const shortInline = shortFlagOf(syntax.inlineFlags, arg.text);
2069
+ if (shortInline) return { script: false, sql: arg.text.slice(shortInline.length), hidden: arg.expands };
2070
+ const cluster = SHORT_FLAG_CLUSTER_ONLY.exec(arg.text)?.[1];
2071
+ const last = cluster ? `-${cluster.slice(-1)}` : "";
2072
+ if (cluster && cluster.length > 1 && syntax.scriptFlags.has(last)) return { script: true, sql: null, hidden: false };
2073
+ if (cluster && cluster.length > 1 && syntax.inlineFlags.has(last)) return valueOf(next);
2074
+ return { script: false, sql: arg.text, hidden: false };
2075
+ };
2076
+ var namesALongFlagValue = (text, flags) => [...flags].some((flag) => flag.startsWith("--") && text.startsWith(`${flag}${FLAG_VALUE_SEPARATOR}`));
2077
+ var positionalSqlIsHidden = (args, syntax) => syntax.positionalSql && args.filter((arg) => !arg.text.startsWith("-")).slice(1).some((arg) => arg.expands);
2078
+ var sqlClientSyntaxOf = ({ program }) => program === void 0 ? void 0 : SQL_CLIENT_SYNTAX.get(basenameOf(program.text));
2079
+ var sqlClientChangesData = (invocation) => {
2080
+ const syntax = sqlClientSyntaxOf(invocation);
2081
+ if (syntax === void 0) return false;
2082
+ return positionalSqlIsHidden(invocation.args, syntax) || invocation.args.some((_, index) => {
2083
+ const { script, sql, hidden } = sqlArgumentAt(invocation.args, index, syntax);
2084
+ return script || hidden || sql !== null && writesThroughSql(sql);
2085
+ });
2086
+ };
2087
+ var PULL_REQUEST_CLIS = /* @__PURE__ */ new Set(["gh", "glab"]);
2088
+ var PULL_REQUEST_NOUNS = /* @__PURE__ */ new Set(["pr", "mr"]);
2089
+ var MERGE_SUBCOMMAND = "merge";
2090
+ var PULL_REQUEST_MERGE_PATH = /(?:^|\/)pulls\/\d+\/merge$/;
2091
+ var mergesAPullRequest = ({ program, args }) => {
2092
+ if (program === void 0 || !PULL_REQUEST_CLIS.has(basenameOf(program.text))) return false;
2093
+ const words = args.filter((arg) => !arg.text.startsWith("-"));
2094
+ 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));
2095
+ };
2096
+ var DEPLOY_AND_SCHEMA_COMMAND_PATTERNS = [
2097
+ "Bash(kubectl apply:*)",
2098
+ "Bash(kubectl replace:*)",
2099
+ "Bash(kubectl scale:*)",
2100
+ "Bash(kubectl rollout restart:*)",
2101
+ "Bash(kubectl rollout undo:*)",
2102
+ "Bash(helm install:*)",
2103
+ "Bash(helm upgrade:*)",
2104
+ "Bash(helm rollback:*)",
2105
+ "Bash(vercel promote:*)",
2106
+ "Bash(vercel rollback:*)",
2107
+ "Bash(railway up:*)",
2108
+ "Bash(prisma db push:*)",
2109
+ "Bash(prisma migrate reset:*)",
2110
+ "Bash(supabase db push:*)",
2111
+ "Bash(supabase db reset:*)",
2112
+ "Bash(drizzle-kit push:*)",
2113
+ "Bash(docker push:*)",
2114
+ "Bash(podman push:*)",
2115
+ "Bash(twine upload:*)",
2116
+ "Bash(npm publish:*)",
2117
+ "Bash(npm unpublish:*)",
2118
+ "Bash(cargo publish:*)",
2119
+ "Bash(gem push:*)",
2120
+ "Bash(poetry publish:*)",
2121
+ "Bash(uv publish:*)",
2122
+ "Bash(pnpm publish:*)",
2123
+ "Bash(yarn publish:*)",
2124
+ "Bash(yarn npm publish:*)",
2125
+ "Bash(bun publish:*)",
2126
+ "Bash(gh release create:*)",
2127
+ "Bash(pulumi up:*)",
2128
+ "Bash(pulumi destroy:*)",
2129
+ "Bash(cdk deploy:*)",
2130
+ "Bash(cdk destroy:*)",
2131
+ "Bash(tofu apply:*)",
2132
+ "Bash(tofu destroy:*)",
2133
+ "Bash(terraform apply:*)",
2134
+ "Bash(terraform destroy:*)",
2135
+ "Bash(firebase deploy:*)",
2136
+ "Bash(fly deploy:*)",
2137
+ "Bash(flyctl deploy:*)",
2138
+ "Bash(wrangler deploy:*)",
2139
+ "Bash(wrangler publish:*)",
2140
+ "Bash(serverless deploy:*)",
2141
+ "Bash(sls deploy:*)",
2142
+ "Bash(gcloud app deploy:*)",
2143
+ "Bash(gcloud run deploy:*)",
2144
+ "Bash(gcloud functions deploy:*)"
2145
+ ];
2146
+ var SECRET_FILE_NAME = /^(?:\.env(?:\.(?!example$|sample$|template$)[\w.-]+)?|(?:secrets?|credentials)\.(?:json|ya?ml|env|toml|ini)|credentials)$/i;
2147
+ var REDIRECTS_INTO_A_SECRET_FILE = />>?\s*(?:[^\s;|&<>]*\/)?\.env(?:\.[\w-]+)?(?=$|[\s;|&)])/;
2148
+ var COPIES_ONTO_ITS_LAST_OPERAND = /* @__PURE__ */ new Set(["cp", "install", "ln", "rsync", "scp"]);
2149
+ var WRITES_EVERY_OPERAND = /* @__PURE__ */ new Set(["mv", "tee", "rm", "unlink", "shred", "truncate"]);
2150
+ var EDITS_IN_PLACE = /* @__PURE__ */ new Set(["sed", "gsed", "perl"]);
2151
+ var IN_PLACE_FLAG = /^(?:-[A-Za-z]*i|--in-place(?:=.*)?)$/;
2152
+ var namesASecretFile = (word) => word !== void 0 && SECRET_FILE_NAME.test(basenameOf(word.text));
2153
+ var NETWORK_SENDERS = /* @__PURE__ */ new Set([
2154
+ "curl",
2155
+ "wget",
2156
+ "scp",
2157
+ "sftp",
2158
+ "rsync",
2159
+ "nc",
2160
+ "ncat",
2161
+ "http",
2162
+ "https",
2163
+ "xh",
2164
+ "gh",
2165
+ "aws",
2166
+ "gsutil",
2167
+ "rclone"
2168
+ ]);
2169
+ var FILE_REFERENCE_PREFIX = /^(?:[\w.-]+=)?@|^[\w.-]+:/;
2170
+ var namesASecretFileReference = (word) => SECRET_FILE_NAME.test(basenameOf(word.text.replace(FILE_REFERENCE_PREFIX, "")));
2171
+ var sendsASecretFile = ({ program, args }) => program !== void 0 && NETWORK_SENDERS.has(basenameOf(program.text)) && args.some(namesASecretFileReference);
2172
+ var writesASecretFile = ({ program, args }) => {
2173
+ if (program === void 0) return false;
2174
+ if (sendsASecretFile({ program, args })) return true;
2175
+ const name = basenameOf(program.text);
2176
+ const operands = args.filter((arg) => !FLAG.test(arg.text));
2177
+ if (COPIES_ONTO_ITS_LAST_OPERAND.has(name)) return namesASecretFile(operands.at(-1));
2178
+ if (WRITES_EVERY_OPERAND.has(name)) return operands.some(namesASecretFile);
2179
+ return EDITS_IN_PLACE.has(name) && args.some((arg) => IN_PLACE_FLAG.test(arg.text)) && operands.some(namesASecretFile);
2180
+ };
1827
2181
  var segmentNeedsAPerson = (toolName, segment) => {
1828
2182
  const invocation = invocationOf(segment);
1829
2183
  const normalized = normalizedInvocation(invocation);
1830
- return invocationHidesItsEffect(invocation) || subcommandDestroys(invocation) || classifyDecisionRisk(toolName, normalized) !== "standard" || isCoveredByPolicy(CAPABILITY_PATTERNS.destructive, toolName, normalized) || isCoveredByPolicy(CAPABILITY_PATTERNS.network, toolName, normalized) || isCoveredByPolicy(DATA_LOSS_COMMAND_PATTERNS, toolName, normalized);
2184
+ return invocationHidesItsEffect(invocation) || subcommandDestroys(invocation) || changesWhatIsDeployed(toolName, invocation, normalized) || sqlClientChangesData(invocation) || classifyDecisionRisk(toolName, normalized) !== "standard" || isCoveredByPolicy(CAPABILITY_PATTERNS.destructive, toolName, normalized) || isCoveredByPolicy(CAPABILITY_PATTERNS.network, toolName, normalized) || isCoveredByPolicy(DATA_LOSS_COMMAND_PATTERNS, toolName, normalized);
1831
2185
  };
1832
2186
  var commandNeedsAPerson = (toolName, command) => {
1833
2187
  if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
1834
2188
  const segments = readableCommandSegments(command);
1835
- return segments === null || classifyDecisionRisk(toolName, command) !== "standard" || segments.some((segment) => segmentNeedsAPerson(toolName, segment));
2189
+ return segments === null || classifyDecisionRisk(toolName, command) !== "standard" || segments.some((segment) => segmentNeedsAPerson(toolName, segment)) || pipesIntoASqlClient(command);
2190
+ };
2191
+ var VISIBLE_DATA_LOSS_PATTERNS = [
2192
+ "Bash(git branch -D:*)",
2193
+ "Bash(git branch --delete:*)",
2194
+ "Bash(git stash drop:*)",
2195
+ "Bash(git stash clear:*)",
2196
+ "Bash(git filter-branch:*)",
2197
+ "Bash(git update-ref -d:*)",
2198
+ "Bash(git reflog expire:*)",
2199
+ "Bash(git prune:*)",
2200
+ "Bash(dd :*)",
2201
+ "Bash(shred:*)",
2202
+ "Bash(chmod -R:*)",
2203
+ "Bash(chown -R:*)",
2204
+ "Bash(dropdb:*)",
2205
+ "Bash(mkfs:*)",
2206
+ "Bash(rclone sync:*)",
2207
+ "Bash(rclone delete:*)",
2208
+ "Bash(rclone purge:*)",
2209
+ "Bash(diskutil eraseDisk:*)",
2210
+ "Bash(diskutil eraseVolume:*)",
2211
+ "Bash(diskutil zeroDisk:*)",
2212
+ "Bash(diskutil secureErase:*)",
2213
+ "Bash(diskutil partitionDisk:*)",
2214
+ "Bash(diskutil reformat:*)"
2215
+ ];
2216
+ var NEW_BRANCH_FLAGS = /* @__PURE__ */ new Set(["-b", "--orphan"]);
2217
+ var CHECKOUT_PATH_FLAGS = /* @__PURE__ */ new Set(["-p", "--patch", "--ours", "--theirs", "--pathspec-from-file"]);
2218
+ var UNSTAGE_ONLY_FLAGS = /* @__PURE__ */ new Set(["--staged", "-S"]);
2219
+ var WORKTREE_FLAGS = /* @__PURE__ */ new Set(["--worktree", "-W"]);
2220
+ var DISCARDING_RESET_MODES = /* @__PURE__ */ new Set(["--hard", "--merge", "--keep"]);
2221
+ var FORCE_FLAGS = /* @__PURE__ */ new Set(["-f", "--force"]);
2222
+ var REFSPEC_THAT_FORCES_OR_DELETES = /^[+:]/;
2223
+ var LOOKS_LIKE_A_PATH = /^\.{1,2}$|\/$|[*?[]|(?:^|\/)[^/]+\.[A-Za-z][A-Za-z0-9]*$/;
2224
+ var GIT_WORK_DISCARDERS = /* @__PURE__ */ new Map([
2225
+ ["checkout", (flags, operands) => !flags.some((flag) => NEW_BRANCH_FLAGS.has(flag)) && (flags.some((flag) => CHECKOUT_PATH_FLAGS.has(flag)) || operands.length > 1 || operands.some((operand) => LOOKS_LIKE_A_PATH.test(operand)))],
2226
+ ["restore", (flags) => !flags.some((flag) => UNSTAGE_ONLY_FLAGS.has(flag)) || flags.some((flag) => WORKTREE_FLAGS.has(flag))],
2227
+ ["reset", (flags) => flags.some((flag) => DISCARDING_RESET_MODES.has(flag))],
2228
+ ["push", (_flags, operands) => operands.some((operand) => REFSPEC_THAT_FORCES_OR_DELETES.test(operand))],
2229
+ ["worktree", (flags, operands) => operands[0] === "remove" && flags.some((flag) => FORCE_FLAGS.has(flag))]
2230
+ ]);
2231
+ var gitDiscardsWork = ({ program, args }) => {
2232
+ if (program === void 0 || basenameOf(program.text) !== "git") return false;
2233
+ const [subcommand, ...rest] = argsAfterHarmlessGlobalFlags(args);
2234
+ const discards = GIT_WORK_DISCARDERS.get(subcommand?.text ?? "");
2235
+ const texts = rest.map((arg) => arg.text);
2236
+ return discards !== void 0 && discards(texts.filter((text) => FLAG.test(text)), texts.filter((text) => !FLAG.test(text)));
2237
+ };
2238
+ var PACKAGE_MANAGERS = /* @__PURE__ */ new Set([
2239
+ "npm",
2240
+ "pnpm",
2241
+ "yarn",
2242
+ "bun",
2243
+ "pip",
2244
+ "pip3",
2245
+ "cargo",
2246
+ "brew",
2247
+ "gem",
2248
+ "uv",
2249
+ "poetry"
2250
+ ]);
2251
+ var GIT = "git";
2252
+ var removesAPackage = ({ program, args }) => program !== void 0 && PACKAGE_MANAGERS.has(basenameOf(program.text)) && subcommandWordDestroys(subcommandWordsOf(argsAfterHarmlessGlobalFlags(args))[0]?.text ?? "");
2253
+ var subcommandDestroysVisibly = (invocation) => subcommandDestroys(invocation) && basenameOf(invocation.program?.text ?? "") !== GIT && !removesAPackage(invocation);
2254
+ var SERVICE_MANAGER_READ_VERBS = /* @__PURE__ */ new Map([
2255
+ ["systemctl", /* @__PURE__ */ new Set([
2256
+ "status",
2257
+ "show",
2258
+ "cat",
2259
+ "help",
2260
+ "list-units",
2261
+ "list-unit-files",
2262
+ "list-timers",
2263
+ "list-sockets",
2264
+ "list-dependencies",
2265
+ "list-jobs",
2266
+ "is-active",
2267
+ "is-enabled",
2268
+ "is-failed",
2269
+ "is-system-running",
2270
+ "get-default"
2271
+ ])],
2272
+ ["launchctl", /* @__PURE__ */ new Set([
2273
+ "list",
2274
+ "print",
2275
+ "print-disabled",
2276
+ "print-cache",
2277
+ "blame",
2278
+ "help",
2279
+ "version",
2280
+ "getenv",
2281
+ "managerpid",
2282
+ "managername",
2283
+ "manageruid",
2284
+ "hostinfo",
2285
+ "dumpstate"
2286
+ ])]
2287
+ ]);
2288
+ var changesAService = ({ program, args }) => {
2289
+ const readVerbs = program === void 0 ? void 0 : SERVICE_MANAGER_READ_VERBS.get(basenameOf(program.text));
2290
+ const verb = args.find((arg) => !FLAG.test(arg.text))?.text;
2291
+ return readVerbs !== void 0 && verb !== void 0 && !readVerbs.has(verb);
2292
+ };
2293
+ var CRONTAB = "crontab";
2294
+ var CRONTAB_USER_FLAG = "-u";
2295
+ var CRONTAB_REMOVE_FLAG = "-r";
2296
+ var replacesTheCrontab = ({ program, args }) => program !== void 0 && basenameOf(program.text) === CRONTAB && args.some((arg, index) => arg.text === CRONTAB_REMOVE_FLAG || arg.text === STANDARD_INPUT_ARGUMENT || !FLAG.test(arg.text) && args[index - 1]?.text !== CRONTAB_USER_FLAG);
2297
+ var FIND_DELETE = "-delete";
2298
+ var FIND_RUNNERS = /* @__PURE__ */ new Set(["-exec", "-execdir", "-ok", "-okdir"]);
2299
+ var FIND_RUNNER_END = /* @__PURE__ */ new Set([";", "+"]);
2300
+ var losesDataVisibly = (toolName, invocation, normalized, depth) => {
2301
+ const { program, args } = invocation;
2302
+ if (program === void 0) return false;
2303
+ const name = basenameOf(program.text);
2304
+ if (name === "find") {
2305
+ return args.some((arg) => arg.text === FIND_DELETE) || args.some((arg, index) => FIND_RUNNERS.has(arg.text) && invocationHasAVisibleConsequence(toolName, {
2306
+ program: args[index + 1],
2307
+ args: args.slice(index + 2, findRunnerEnd(args, index + 2))
2308
+ }, depth + 1));
2309
+ }
2310
+ const destructiveArguments = destructiveArgumentsOf(name, args);
2311
+ 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);
2312
+ };
2313
+ var findRunnerEnd = (args, from) => {
2314
+ const end = args.findIndex((arg, index) => index >= from && FIND_RUNNER_END.has(arg.text));
2315
+ return end === -1 ? args.length : end;
2316
+ };
2317
+ var wrapperSyntax = (optionsWithAValue = [], operandsBeforeTheCommand = 0) => ({ optionsWithAValue: new Set(optionsWithAValue), operandsBeforeTheCommand });
2318
+ var ENVIRONMENT_ASSIGNMENTS = wrapperSyntax();
2319
+ var WRAPPER_SYNTAX = /* @__PURE__ */ new Map([
2320
+ ["env", wrapperSyntax(["-u", "--unset", "-C", "--chdir", "-S", "--split-string", "-P"])],
2321
+ ["xargs", wrapperSyntax([
2322
+ "-I",
2323
+ "-J",
2324
+ "-R",
2325
+ "-S",
2326
+ "-n",
2327
+ "-L",
2328
+ "-P",
2329
+ "-s",
2330
+ "-d",
2331
+ "-E",
2332
+ "-a",
2333
+ "--max-args",
2334
+ "--max-lines",
2335
+ "--max-procs",
2336
+ "--max-chars",
2337
+ "--delimiter",
2338
+ "--arg-file",
2339
+ "--eof",
2340
+ "--replace"
2341
+ ])],
2342
+ ["timeout", wrapperSyntax(["-s", "--signal", "-k", "--kill-after"], 1)],
2343
+ ["nice", wrapperSyntax(["-n", "--adjustment"])],
2344
+ ["ionice", wrapperSyntax(["-c", "--class", "-n", "--classdata"])],
2345
+ ["stdbuf", wrapperSyntax(["-i", "-o", "-e", "--input", "--output", "--error"])],
2346
+ ["flock", wrapperSyntax(["-w", "--wait", "--timeout", "-E", "--conflict-exit-code"], 1)],
2347
+ ["caffeinate", wrapperSyntax(["-t", "-w"])],
2348
+ ["sudo", wrapperSyntax([
2349
+ "-u",
2350
+ "--user",
2351
+ "-g",
2352
+ "--group",
2353
+ "-C",
2354
+ "--close-from",
2355
+ "-h",
2356
+ "--host",
2357
+ "-p",
2358
+ "--prompt",
2359
+ "-U",
2360
+ "--other-user",
2361
+ "-r",
2362
+ "--role",
2363
+ "-t",
2364
+ "--type",
2365
+ "-D",
2366
+ "--chdir"
2367
+ ])],
2368
+ ["doas", wrapperSyntax(["-u", "-C"])],
2369
+ ["time", wrapperSyntax(["-f", "--format", "-o", "--output"])],
2370
+ ["watch", wrapperSyntax(["-n", "--interval"])],
2371
+ ["nohup", wrapperSyntax()],
2372
+ ["command", wrapperSyntax()],
2373
+ ["builtin", wrapperSyntax()],
2374
+ ["exec", wrapperSyntax(["-a"])],
2375
+ ["unbuffer", wrapperSyntax()],
2376
+ ["npx", wrapperSyntax(["-p", "--package", "-c", "--call"])],
2377
+ ["bunx", wrapperSyntax(["-p", "--package"])],
2378
+ ["pnpx", wrapperSyntax()],
2379
+ ["uvx", wrapperSyntax(["--from", "--with", "-p", "--python"])]
2380
+ ]);
2381
+ var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh"]);
2382
+ var SCRIPT_FLAG_CLUSTER = /^-[A-Za-z]*c$/;
2383
+ var EVAL = "eval";
2384
+ var MAX_CONSEQUENCE_DEPTH = 4;
2385
+ var commandStartAfterWrapper = (words, syntax) => {
2386
+ let index = 0;
2387
+ while (index < words.length) {
2388
+ const text = words[index]?.text ?? "";
2389
+ if (text === END_OF_OPTIONS) return index + 1 + syntax.operandsBeforeTheCommand;
2390
+ if (!SHELL_ENV_ASSIGNMENT.test(text) && (!FLAG.test(text) || text === STANDARD_INPUT_ARGUMENT)) break;
2391
+ index += syntax.optionsWithAValue.has(text) ? 2 : 1;
2392
+ }
2393
+ return index + syntax.operandsBeforeTheCommand;
2394
+ };
2395
+ var wrappedInvocation = ({ program, args }) => {
2396
+ if (program === void 0) return null;
2397
+ const assigns = SHELL_ENV_ASSIGNMENT.test(program.text);
2398
+ const syntax = assigns ? ENVIRONMENT_ASSIGNMENTS : WRAPPER_SYNTAX.get(basenameOf(program.text));
2399
+ if (syntax === void 0) return null;
2400
+ const words = assigns ? [program, ...args] : args;
2401
+ const start = commandStartAfterWrapper(words, syntax);
2402
+ const command = words[start];
2403
+ return command === void 0 ? null : { program: command, args: words.slice(start + 1) };
2404
+ };
2405
+ var innermostInvocation = (invocation, depth = 0) => {
2406
+ const wrapped = depth < MAX_CONSEQUENCE_DEPTH ? wrappedInvocation(invocation) : null;
2407
+ return wrapped === null ? invocation : innermostInvocation(wrapped, depth + 1);
2408
+ };
2409
+ var shellScriptOf = ({ program, args }) => {
2410
+ if (program === void 0) return null;
2411
+ if (basenameOf(program.text) === EVAL) return args.some((arg) => arg.expands) ? null : args.map((arg) => arg.text).join(" ");
2412
+ if (!SHELLS.has(basenameOf(program.text))) return null;
2413
+ const flag = args.findIndex((arg) => SCRIPT_FLAG_CLUSTER.test(arg.text));
2414
+ const script = flag === -1 ? void 0 : args[flag + 1];
2415
+ return script === void 0 || script.expands ? null : script.text;
2416
+ };
2417
+ var FLAG = /^-/;
2418
+ var FLAG_WITH_ATTACHED_VALUE = /^-[^=]*=/;
2419
+ var withoutLeadingFlags = (args, flagsTakeAValue) => {
2420
+ let index = 0;
2421
+ while (index < args.length && FLAG.test(args[index]?.text ?? "")) {
2422
+ const takesNext = flagsTakeAValue && !FLAG_WITH_ATTACHED_VALUE.test(args[index]?.text ?? "") && !FLAG.test(args[index + 1]?.text ?? "-");
2423
+ index += takesNext ? 2 : 1;
2424
+ }
2425
+ return args.slice(index);
2426
+ };
2427
+ var readingsPastGlobalFlags = ({ program, args }) => {
2428
+ const meaningful = argsAfterHarmlessGlobalFlags(args);
2429
+ if (program === void 0 || !SUBCOMMAND_CLIS.has(basenameOf(program.text)) || !FLAG.test(meaningful[0]?.text ?? "")) return [];
2430
+ return [false, true].map((flagsTakeAValue) => ({ program, args: withoutLeadingFlags(meaningful, flagsTakeAValue) }));
2431
+ };
2432
+ var PREVIEW_FLAG = /^--(?:help|dry-?run(?:=(?:client|server|true))?)$/;
2433
+ var TASK_RUNNERS = /* @__PURE__ */ new Set(["make", "just", "rake", "task"]);
2434
+ var PUBLISH_SUBCOMMAND = "publish";
2435
+ var honoursPreviewFlags = ({ program, args }) => {
2436
+ const name = basenameOf(program?.text ?? "");
2437
+ if (TASK_RUNNERS.has(name)) return false;
2438
+ return !PACKAGE_MANAGERS.has(name) || subcommandWordsOf(argsAfterHarmlessGlobalFlags(args))[0]?.text === PUBLISH_SUBCOMMAND;
2439
+ };
2440
+ var onlyPreviews = (invocation) => invocation.program !== void 0 && honoursPreviewFlags(invocation) && invocation.args.some((arg) => !arg.expands && PREVIEW_FLAG.test(arg.text));
2441
+ var changesWhatIsDeployed = (toolName, invocation, normalized) => !onlyPreviews(invocation) && (changesProduction(invocation) || mergesAPullRequest(invocation) || isCoveredByPolicy(DEPLOY_AND_SCHEMA_COMMAND_PATTERNS, toolName, normalized));
2442
+ var invocationHasAVisibleConsequence = (toolName, invocation, depth) => {
2443
+ if (invocation.program === void 0 || depth > MAX_CONSEQUENCE_DEPTH) return false;
2444
+ const normalized = quotedInvocation(invocation);
2445
+ const wrapped = wrappedInvocation(invocation);
2446
+ const script = shellScriptOf(invocation);
2447
+ return classifyDecisionRisk(toolName, normalized) === "destructive" || changesWhatIsDeployed(toolName, invocation, normalized) || writesASecretFile(invocation) || losesDataVisibly(toolName, invocation, normalized, depth) || subcommandDestroysVisibly(invocation) || sqlClientChangesData(invocation) || wrapped !== null && invocationHasAVisibleConsequence(toolName, wrapped, depth + 1) || script !== null && commandHasAVisibleConsequenceAt(toolName, script, depth + 1) || readingsPastGlobalFlags(invocation).some((reading) => invocationHasAVisibleConsequence(toolName, reading, depth + 1));
2448
+ };
2449
+ var RUNS_FETCHED_CODE = /(?:\$\(|`|<\()\s*(?:curl|wget)\b/;
2450
+ var FETCHING_PROGRAMS = /* @__PURE__ */ new Set(["curl", "wget"]);
2451
+ var READS_ITS_SCRIPT_FROM_STANDARD_INPUT = /^-[A-Za-z]*s[A-Za-z]*$/;
2452
+ var interpreterNameOf = (program) => basenameOf(program.text).replace(/\d+(?:\.\d+)*$/, "");
2453
+ var runsScripts = (program) => SHELLS.has(basenameOf(program.text)) || INLINE_CODE_INTERPRETERS.has(interpreterNameOf(program));
2454
+ var runsWhatItReads = ({ program, args }) => program !== void 0 && runsScripts(program) && (args.length === 0 || args.every((arg) => arg.text.startsWith("-")) || args.some((arg) => arg.text === STANDARD_INPUT_ARGUMENT) || SHELLS.has(basenameOf(program.text)) && args.some((arg) => READS_ITS_SCRIPT_FROM_STANDARD_INPUT.test(arg.text)));
2455
+ var fetches = (invocation) => FETCHING_PROGRAMS.has(basenameOf(innermostInvocation(invocation).program?.text ?? ""));
2456
+ var URL_QUERY_OR_FRAGMENT = /[?#].*$/;
2457
+ var SOURCES_A_SCRIPT = /* @__PURE__ */ new Set(["source", "."]);
2458
+ var downloadedNamesOf = (invocations) => new Set(invocations.filter(fetches).flatMap((invocation) => innermostInvocation(invocation).args.filter((arg) => !FLAG.test(arg.text)).map((arg) => basenameOf(arg.text.replace(URL_QUERY_OR_FRAGMENT, "")))));
2459
+ var runsADownloadedScript = (invocations) => {
2460
+ const downloaded = downloadedNamesOf(invocations);
2461
+ return downloaded.size > 0 && invocations.map((invocation) => innermostInvocation(invocation)).some(({ program, args }) => {
2462
+ if (program === void 0) return false;
2463
+ const script = args.find((arg) => !FLAG.test(arg.text))?.text;
2464
+ return downloaded.has(basenameOf(program.text)) || (runsScripts(program) || SOURCES_A_SCRIPT.has(program.text)) && script !== void 0 && downloaded.has(basenameOf(script));
2465
+ });
2466
+ };
2467
+ var pipedSegmentsOf = (command) => (shellSegmentsReadingAPipe(commandForReading(command)) ?? []).map((segment) => innermostInvocation(invocationOf(withoutShellGrouping(segment))));
2468
+ var pipesIntoASqlClient = (command) => pipedSegmentsOf(command).some((invocation) => sqlClientSyntaxOf(invocation) !== void 0);
2469
+ var pipesFetchedCodeIntoAnInterpreter = (command, invocations) => invocations.some(fetches) && pipedSegmentsOf(command).some(runsWhatItReads);
2470
+ var SHELL_GROUPING_PREFIX = /^\s*(?:(?:if|then|elif|else|do|while|until|!)\s+|[({]\s*)+/;
2471
+ var GROUP_OPENER_BY_CLOSER = /* @__PURE__ */ new Map([[")", "("], ["}", "{"]]);
2472
+ var occurrencesOf = (text, character) => text.split(character).length - 1;
2473
+ var closesAGroupItDidNotOpen = (text) => {
2474
+ const last = text.at(-1) ?? "";
2475
+ const opener = GROUP_OPENER_BY_CLOSER.get(last);
2476
+ return opener !== void 0 && occurrencesOf(text, last) > occurrencesOf(text, opener);
2477
+ };
2478
+ var withoutShellGrouping = (segment) => {
2479
+ let text = segment.replace(SHELL_GROUPING_PREFIX, "").trimEnd();
2480
+ while (closesAGroupItDidNotOpen(text)) text = text.slice(0, -1).trimEnd();
2481
+ return text;
2482
+ };
2483
+ var HEREDOC_OPERATOR = /(?<!<)\d*<<(?!<)-?[ \t]*(?:'([A-Za-z_]\w*)'|"([A-Za-z_]\w*)"|\\?([A-Za-z_]\w*))/g;
2484
+ var HEREDOC_MARK = "\uE001";
2485
+ var withHeredocBodiesSetAside = (command) => {
2486
+ const lines = command.split("\n");
2487
+ const kept = [];
2488
+ const bodies = [];
2489
+ for (let index = 0; index < lines.length; index += 1) {
2490
+ const line = lines[index] ?? "";
2491
+ const delimiters = [...line.matchAll(HEREDOC_OPERATOR)].map((match) => match[1] ?? match[2] ?? match[3] ?? "");
2492
+ kept.push(line.replace(HEREDOC_OPERATOR, ` ${HEREDOC_MARK} `));
2493
+ for (const delimiter of delimiters) {
2494
+ const body = [];
2495
+ for (index += 1; index < lines.length && !mightEndAHeredoc(lines[index] ?? "", delimiter); index += 1) {
2496
+ body.push(lines[index] ?? "");
2497
+ }
2498
+ bodies.push(body.join("\n"));
2499
+ const ending = lines[index];
2500
+ if (ending !== void 0 && !isHeredocTerminator(ending, delimiter)) kept.push(ending);
2501
+ }
2502
+ }
2503
+ return { text: kept.join("\n"), bodies };
2504
+ };
2505
+ var REDIRECTION_TARGET = String.raw`(?:'[^']*'|"[^"]*"|[^\s;|&()<>]+)`;
2506
+ var INPUT_REDIRECTION = new RegExp(String.raw`\d*(?:<<<|<(?![<(&>]))\s*${REDIRECTION_TARGET}`, "g");
2507
+ var OUTPUT_REDIRECTION = new RegExp(String.raw`(?:\d*|&)(?:>>?|>\||<>|>&|<&)\s*${REDIRECTION_TARGET}`, "g");
2508
+ var REDIRECTED_INPUT_MARK = "\uE000";
2509
+ var ROUGH_SEPARATORS = ["&&", "||", "$(", "\n", ";", "|", "&", "`", "(", ")", "{", "}"];
2510
+ var SEPARATORS_INSIDE_DOUBLE_QUOTES = /* @__PURE__ */ new Set(["$(", "`", ")"]);
2511
+ var PIPE2 = "|";
2512
+ var SUBSTITUTION_OPENERS = /* @__PURE__ */ new Set(["$(", "`"]);
2513
+ var QUOTE_CHARACTERS = /['"]/g;
2514
+ var roughPiecesOf = (text) => {
2515
+ const pieces = [];
2516
+ let current = "";
2517
+ let before = "";
2518
+ let quote = null;
2519
+ for (let index = 0; index < text.length; index += 1) {
2520
+ const char = text.charAt(index);
2521
+ if (quote === "'") {
2522
+ current += char;
2523
+ if (char === "'") quote = null;
2524
+ continue;
2525
+ }
2526
+ if (char === "\\") {
2527
+ current += text.slice(index, index + 2);
2528
+ index += 1;
2529
+ continue;
2530
+ }
2531
+ const separator = ROUGH_SEPARATORS.find((candidate) => text.startsWith(candidate, index) && (quote === null || SEPARATORS_INSIDE_DOUBLE_QUOTES.has(candidate)));
2532
+ if (separator !== void 0) {
2533
+ pieces.push({ text: current, before });
2534
+ current = "";
2535
+ before = separator;
2536
+ index += separator.length - 1;
2537
+ continue;
2538
+ }
2539
+ if (char === '"' || char === "'") quote = quote === char ? null : quote ?? char;
2540
+ current += char;
2541
+ }
2542
+ return [...pieces, { text: current, before }];
2543
+ };
2544
+ var invocationOfRoughPiece = (text) => {
2545
+ const piece = withoutShellGrouping(text.replaceAll(REDIRECTED_INPUT_MARK, "").replaceAll(HEREDOC_MARK, ""));
2546
+ const parsed = invocationOf(piece);
2547
+ return parsed.program !== void 0 ? parsed : invocationOf(piece.replace(QUOTE_CHARACTERS, ""));
2548
+ };
2549
+ var roughSegmentsOf = (command) => {
2550
+ const { text, bodies } = withHeredocBodiesSetAside(commandForReading(command));
2551
+ const pieces = roughPiecesOf(text.replace(INPUT_REDIRECTION, ` ${REDIRECTED_INPUT_MARK} `).replace(OUTPUT_REDIRECTION, " "));
2552
+ let heredocsSeen = 0;
2553
+ return pieces.map((piece, index) => {
2554
+ const heredocCount = occurrencesOf(piece.text, HEREDOC_MARK);
2555
+ const heredocs = bodies.slice(heredocsSeen, heredocsSeen + heredocCount);
2556
+ heredocsSeen += heredocCount;
2557
+ return {
2558
+ invocation: invocationOfRoughPiece(piece.text),
2559
+ readsInput: piece.text.includes(REDIRECTED_INPUT_MARK) || heredocCount > 0 || piece.before === PIPE2,
2560
+ readsAPipe: piece.before === PIPE2,
2561
+ feedsASubstitution: SUBSTITUTION_OPENERS.has(pieces[index + 1]?.before ?? ""),
2562
+ heredocs
2563
+ };
2564
+ });
2565
+ };
2566
+ var roughSegmentHasAVisibleConsequence = (toolName, segment, fetchesCode, depth) => {
2567
+ const innermost = innermostInvocation(segment.invocation);
2568
+ const runsAHeredocScript = innermost.program !== void 0 && SHELLS.has(basenameOf(innermost.program.text));
2569
+ 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));
2570
+ };
2571
+ var roughReadingHasAVisibleConsequence = (toolName, command, depth) => {
2572
+ const segments = roughSegmentsOf(command);
2573
+ const invocations = segments.map((segment) => segment.invocation);
2574
+ const fetchesCode = invocations.some(fetches);
2575
+ return segments.some((segment) => roughSegmentHasAVisibleConsequence(toolName, segment, fetchesCode, depth)) || runsADownloadedScript(invocations);
2576
+ };
2577
+ var commandHasAVisibleConsequenceAt = (toolName, command, depth) => {
2578
+ if (toolName === "Bash" && isSafeReadOnlyCommand(command)) return false;
2579
+ const readable = withHeredocSubstitutionsAsQuotedText(command);
2580
+ if (classifyDecisionRisk(toolName, readable) === "destructive" || RUNS_FETCHED_CODE.test(readable) || REDIRECTS_INTO_A_SECRET_FILE.test(readable)) return true;
2581
+ const segments = readableCommandSegments(command);
2582
+ if (segments === null) return roughReadingHasAVisibleConsequence(toolName, command, depth);
2583
+ const invocations = segments.map((segment) => invocationOf(withoutShellGrouping(segment)));
2584
+ return invocations.some((invocation) => invocationHasAVisibleConsequence(toolName, invocation, depth)) || pipesIntoASqlClient(command) || pipesFetchedCodeIntoAnInterpreter(command, invocations) || runsADownloadedScript(invocations);
1836
2585
  };
2586
+ var commandHasAVisibleConsequence = (toolName, command) => commandHasAVisibleConsequenceAt(toolName, command, 0);
1837
2587
  var withoutSessionToolGrants = (config) => ({
1838
2588
  ...config,
1839
2589
  policies: config.policies.filter((policy) => !isSessionToolGrant(policy))
1840
2590
  });
1841
2591
  var SESSION_TOOL_GRANT_TOOLS = /* @__PURE__ */ new Set(["Bash"]);
2592
+ var isStandingShellAllow = (policy) => !policy.origin && !isSessionToolGrant(policy) && SESSION_TOOL_GRANT_TOOLS.has(policy.tool) && isAutoApprove(policy);
1842
2593
  var MCP_TOOL_PREFIX = "mcp__";
1843
2594
  var parseMcpToolName = (toolName) => {
1844
2595
  if (!toolName.startsWith(MCP_TOOL_PREFIX)) return void 0;
@@ -2460,6 +3211,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
2460
3211
  };
2461
3212
  const governedBySpecificRule = isSpecificArgumentRank(match?.rank);
2462
3213
  const governedBySessionToolGrant = match?.rank === "tool" && isSessionToolGrant(match.policy);
3214
+ const governedByStandingShellAllow = match?.rank === "tool" && isStandingShellAllow(match.policy);
2463
3215
  const governingMode = base.mode;
2464
3216
  const approvedByReadOnlyFloor = !isAutoDeny(base) && (shellReadOnlyFloorApplies(toolName, arg, match?.rank) || coordinationFloorApplies(toolName, match?.rank));
2465
3217
  if (governedBySessionToolGrant && !approvedByReadOnlyFloor && typeof arg === "string" && commandNeedsAPerson(toolName, arg)) {
@@ -2469,7 +3221,7 @@ var resolvePolicy = (config, toolName, modeOverride, toolInput, cwd, repoKey, se
2469
3221
  base = autoApprove(base.tool);
2470
3222
  }
2471
3223
  const wouldAutoApprove = isAutoApprove(base);
2472
- if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && classifyDecisionRisk(toolName, arg) === "destructive") {
3224
+ if (!governedBySpecificRule && wouldAutoApprove && toolName === "Bash" && typeof arg === "string" && (classifyDecisionRisk(toolName, arg) === "destructive" || governedByStandingShellAllow && commandHasAVisibleConsequence(toolName, arg))) {
2473
3225
  base = {
2474
3226
  tool: base.tool,
2475
3227
  timeoutSeconds: config.defaultTimeoutSeconds,