@ordewell/core 0.4.8 → 0.4.9

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.
package/dist/index.js CHANGED
@@ -166,6 +166,7 @@ __export(index_exports, {
166
166
  emptyWarnings: () => emptyWarnings,
167
167
  enabledRunners: () => enabledRunners,
168
168
  ensureDir: () => ensureDir,
169
+ ensureStateDirIgnored: () => ensureStateDirIgnored,
169
170
  escapeControlCharsInStrings: () => escapeControlCharsInStrings,
170
171
  executionLogBlock: () => executionLogBlock,
171
172
  executionSummary: () => executionSummary,
@@ -642,13 +643,1233 @@ var AUTO_COMMANDS = [
642
643
  "jq",
643
644
  "yq"
644
645
  ];
646
+ var UNIVERSAL_FLAGS = ["--help", "--version"];
647
+ var COUNT_FLAG = /^-\d+$/;
648
+ var FLAG_POLICY = {
649
+ ls: {
650
+ cluster: true,
651
+ booleans: [
652
+ "-1",
653
+ "-a",
654
+ "-A",
655
+ "-b",
656
+ "-c",
657
+ "-C",
658
+ "-d",
659
+ "-D",
660
+ "-f",
661
+ "-F",
662
+ "-g",
663
+ "-G",
664
+ "-h",
665
+ "-H",
666
+ "-i",
667
+ "-k",
668
+ "-l",
669
+ "-L",
670
+ "-m",
671
+ "-n",
672
+ "-N",
673
+ "-o",
674
+ "-p",
675
+ "-q",
676
+ "-Q",
677
+ "-r",
678
+ "-R",
679
+ "-s",
680
+ "-S",
681
+ "-t",
682
+ "-u",
683
+ "-U",
684
+ "-v",
685
+ "-x",
686
+ "-X",
687
+ "-Z",
688
+ "--all",
689
+ "--almost-all",
690
+ "--author",
691
+ "--escape",
692
+ "--classify",
693
+ "--file-type",
694
+ "--full-time",
695
+ "--no-group",
696
+ "--human-readable",
697
+ "--si",
698
+ "--dereference-command-line",
699
+ "--inode",
700
+ "--kibibytes",
701
+ "--dereference",
702
+ "--numeric-uid-gid",
703
+ "--literal",
704
+ "--hide-control-chars",
705
+ "--show-control-chars",
706
+ "--quote-name",
707
+ "--reverse",
708
+ "--recursive",
709
+ "--size",
710
+ "--directory",
711
+ "--group-directories-first",
712
+ "--context",
713
+ "--zero"
714
+ ],
715
+ values: [
716
+ "-w",
717
+ "-I",
718
+ "-T",
719
+ "--width",
720
+ "--ignore",
721
+ "--hide",
722
+ "--block-size",
723
+ "--color",
724
+ "--format",
725
+ "--sort",
726
+ "--time",
727
+ "--time-style",
728
+ "--indicator-style",
729
+ "--quoting-style",
730
+ "--tabsize"
731
+ ]
732
+ },
733
+ tree: {
734
+ cluster: true,
735
+ // `-o` is absent on purpose: it is tree's write-to-a-file flag, which the
736
+ // redirect check never sees because there is no redirect.
737
+ booleans: [
738
+ "-a",
739
+ "-d",
740
+ "-f",
741
+ "-i",
742
+ "-l",
743
+ "-x",
744
+ "-C",
745
+ "-n",
746
+ "-p",
747
+ "-s",
748
+ "-h",
749
+ "-u",
750
+ "-g",
751
+ "-D",
752
+ "-t",
753
+ "-c",
754
+ "-r",
755
+ "-F",
756
+ "-q",
757
+ "-N",
758
+ "-Q",
759
+ "-v",
760
+ "-J",
761
+ "-X",
762
+ "-Z",
763
+ "--noreport",
764
+ "--dirsfirst",
765
+ "--du",
766
+ "--prune",
767
+ "--matchdirs",
768
+ "--inodes",
769
+ "--device",
770
+ "--si",
771
+ "--gitignore",
772
+ "--ignore-case",
773
+ "--filesfirst",
774
+ "--nolinks",
775
+ "--hintro"
776
+ ],
777
+ values: [
778
+ "-L",
779
+ "-P",
780
+ "-I",
781
+ "-H",
782
+ "--filelimit",
783
+ "--timefmt",
784
+ "--charset",
785
+ "--sort",
786
+ "--gitfile",
787
+ "--info",
788
+ "--prefix"
789
+ ]
790
+ },
791
+ git: {
792
+ cluster: true,
793
+ // Absent on purpose, and each for a reason that was demonstrated rather than
794
+ // guessed at:
795
+ // -c, --config-env set configuration inline, and several configuration
796
+ // keys name a program to run (`core.pager`, `core.editor`,
797
+ // `diff.external`), so this is an execution flag with one
798
+ // indirection in front of it.
799
+ // --exec-path redirects where git finds its helper programs; a
800
+ // fabricated helper ran during an ordinary remote listing.
801
+ // -O, --open-files-in-pager runs an arbitrary named program over search hits.
802
+ // --upload-pack, --receive-pack name the program run on the other end.
803
+ // --textconv, --ext-diff run filters the repository itself configures.
804
+ // --git-dir, --work-tree repoint git at a tree whose configuration is not
805
+ // the one the developer is looking at.
806
+ // -o, --output write a file. `git ls-files -o` is a legitimate spelling
807
+ // of `--others`, but `git archive -o` writes, and the
808
+ // long spelling stays available for the read.
809
+ booleans: [
810
+ "-a",
811
+ "-b",
812
+ "-E",
813
+ "-F",
814
+ "-h",
815
+ "-H",
816
+ "-i",
817
+ "-I",
818
+ "-l",
819
+ "-p",
820
+ "-P",
821
+ "-q",
822
+ "-r",
823
+ "-R",
824
+ "-s",
825
+ "-t",
826
+ "-u",
827
+ "-v",
828
+ "-w",
829
+ "-z",
830
+ "--oneline",
831
+ "--graph",
832
+ "--stat",
833
+ "--shortstat",
834
+ "--numstat",
835
+ "--dirstat",
836
+ "--summary",
837
+ "--name-only",
838
+ "--name-status",
839
+ "--raw",
840
+ "--patch",
841
+ "--no-patch",
842
+ "--patch-with-stat",
843
+ "--cached",
844
+ "--staged",
845
+ "--porcelain",
846
+ "--short",
847
+ "--long",
848
+ "--branch",
849
+ "--show-stash",
850
+ "--show-current",
851
+ "--all",
852
+ "--tags",
853
+ "--heads",
854
+ "--remotes",
855
+ "--list",
856
+ "--verbose",
857
+ "--quiet",
858
+ "--abbrev-ref",
859
+ "--count",
860
+ "--reverse",
861
+ "--first-parent",
862
+ "--no-merges",
863
+ "--merges",
864
+ "--follow",
865
+ "--full-history",
866
+ "--simplify-merges",
867
+ "--sparse",
868
+ "--dense",
869
+ "--ancestry-path",
870
+ "--left-right",
871
+ "--cherry",
872
+ "--cherry-pick",
873
+ "--cherry-mark",
874
+ "--boundary",
875
+ "--topo-order",
876
+ "--date-order",
877
+ "--author-date-order",
878
+ "--relative-date",
879
+ "--parents",
880
+ "--children",
881
+ "--timestamp",
882
+ "--no-walk",
883
+ "--do-walk",
884
+ "--decorate",
885
+ "--no-decorate",
886
+ "--abbrev-commit",
887
+ "--no-abbrev-commit",
888
+ "--abbrev",
889
+ "--no-abbrev",
890
+ "--color",
891
+ "--no-color",
892
+ "--pretty",
893
+ "--format",
894
+ "--expand-tabs",
895
+ "--no-expand-tabs",
896
+ "--numbered",
897
+ "--email",
898
+ "--no-notes",
899
+ "--notes",
900
+ "--show-signature",
901
+ "--find-renames",
902
+ "--find-copies",
903
+ "--find-copies-harder",
904
+ "--break-rewrites",
905
+ "--no-renames",
906
+ "--irreversible-delete",
907
+ "--full-index",
908
+ "--binary",
909
+ "--text",
910
+ "--check",
911
+ "--exit-code",
912
+ "--word-diff",
913
+ "--function-context",
914
+ "--ignore-all-space",
915
+ "--ignore-space-change",
916
+ "--ignore-space-at-eol",
917
+ "--ignore-blank-lines",
918
+ "--ignore-cr-at-eol",
919
+ "--no-prefix",
920
+ "--default-prefix",
921
+ "--no-textconv",
922
+ "--no-ext-diff",
923
+ "--submodule",
924
+ "--ignore-submodules",
925
+ "--recurse-submodules",
926
+ "--no-recurse-submodules",
927
+ "--others",
928
+ "--deleted",
929
+ "--modified",
930
+ "--unmerged",
931
+ "--stage",
932
+ "--killed",
933
+ "--ignored",
934
+ "--exclude-standard",
935
+ "--error-unmatch",
936
+ "--full-name",
937
+ "--directory",
938
+ "--no-empty-directory",
939
+ "--untracked-files",
940
+ "--no-optional-locks",
941
+ "--no-pager",
942
+ "--paginate",
943
+ "--bare",
944
+ "--literal-pathspecs",
945
+ "--glob-pathspecs",
946
+ "--noglob-pathspecs",
947
+ "--icase-pathspecs",
948
+ "--line-number",
949
+ "--column",
950
+ "--heading",
951
+ "--no-index",
952
+ "--untracked",
953
+ "--no-recursive",
954
+ "--name-rev",
955
+ "--root",
956
+ "--dirty",
957
+ "--broken",
958
+ "--always",
959
+ "--merged",
960
+ "--no-merged",
961
+ "--show-toplevel",
962
+ "--show-cdup",
963
+ "--git-common-dir",
964
+ "--absolute-git-dir",
965
+ "--is-inside-work-tree",
966
+ "--is-inside-git-dir",
967
+ "--is-bare-repository",
968
+ "--symbolic",
969
+ "--symbolic-full-name",
970
+ "--verify",
971
+ "--short-hash",
972
+ "--textual",
973
+ "--batch",
974
+ "--batch-check",
975
+ "--batch-all-objects",
976
+ "--buffer",
977
+ "--follow-symlinks",
978
+ "--use-mailmap"
979
+ ],
980
+ values: [
981
+ "-n",
982
+ "-C",
983
+ "-B",
984
+ "-M",
985
+ "-L",
986
+ "-S",
987
+ "-G",
988
+ "-U",
989
+ "-e",
990
+ "-m",
991
+ "-f",
992
+ "-g",
993
+ "-x",
994
+ "-X",
995
+ "-W",
996
+ "--max-count",
997
+ "--skip",
998
+ "--since",
999
+ "--after",
1000
+ "--until",
1001
+ "--before",
1002
+ "--author",
1003
+ "--grep",
1004
+ "--grep-reflog",
1005
+ "--committer",
1006
+ "--date",
1007
+ "--diff-filter",
1008
+ "--unified",
1009
+ "--stat-width",
1010
+ "--stat-name-width",
1011
+ "--stat-count",
1012
+ "--word-diff-regex",
1013
+ "--line-prefix",
1014
+ "--src-prefix",
1015
+ "--dst-prefix",
1016
+ "--pickaxe-regex",
1017
+ "--anchored",
1018
+ "--color-moved",
1019
+ "--color-words",
1020
+ "--inter-hunk-context",
1021
+ "--max-parents",
1022
+ "--min-parents",
1023
+ "--simplify-by-decoration",
1024
+ "--glob",
1025
+ "--exclude",
1026
+ "--sort",
1027
+ "--contains",
1028
+ "--points-at",
1029
+ "--decorate-refs",
1030
+ "--decorate-refs-exclude",
1031
+ "--encoding",
1032
+ "--context",
1033
+ "--max-depth",
1034
+ // `--filters` is absent from this list on purpose: it runs the clean and
1035
+ // smudge filters the repository under research configures, which is the
1036
+ // same execution vector as the configuration flag above with a different
1037
+ // spelling.
1038
+ "--and",
1039
+ "--or",
1040
+ "--not",
1041
+ "--all-match",
1042
+ "--threads",
1043
+ "--path"
1044
+ ],
1045
+ patterns: [COUNT_FLAG]
1046
+ },
1047
+ wc: {
1048
+ cluster: true,
1049
+ booleans: [
1050
+ "-c",
1051
+ "-l",
1052
+ "-m",
1053
+ "-w",
1054
+ "-L",
1055
+ "--bytes",
1056
+ "--chars",
1057
+ "--lines",
1058
+ "--words",
1059
+ "--max-line-length",
1060
+ "--total"
1061
+ ]
1062
+ },
1063
+ du: {
1064
+ cluster: true,
1065
+ booleans: [
1066
+ "-0",
1067
+ "-a",
1068
+ "-b",
1069
+ "-c",
1070
+ "-D",
1071
+ "-h",
1072
+ "-H",
1073
+ "-k",
1074
+ "-l",
1075
+ "-L",
1076
+ "-m",
1077
+ "-P",
1078
+ "-s",
1079
+ "-S",
1080
+ "-x",
1081
+ "--all",
1082
+ "--apparent-size",
1083
+ "--bytes",
1084
+ "--total",
1085
+ "--dereference-args",
1086
+ "--human-readable",
1087
+ "--si",
1088
+ "--inodes",
1089
+ "--count-links",
1090
+ "--dereference",
1091
+ "--no-dereference",
1092
+ "--separate-dirs",
1093
+ "--summarize",
1094
+ "--one-file-system",
1095
+ "--null"
1096
+ ],
1097
+ values: [
1098
+ "-d",
1099
+ "-t",
1100
+ "-B",
1101
+ "-X",
1102
+ "--max-depth",
1103
+ "--threshold",
1104
+ "--block-size",
1105
+ "--exclude",
1106
+ "--exclude-from",
1107
+ "--time",
1108
+ "--time-style",
1109
+ "--files0-from"
1110
+ ]
1111
+ },
1112
+ df: {
1113
+ cluster: true,
1114
+ booleans: [
1115
+ "-a",
1116
+ "-h",
1117
+ "-H",
1118
+ "-i",
1119
+ "-k",
1120
+ "-l",
1121
+ "-m",
1122
+ "-P",
1123
+ "-T",
1124
+ "-v",
1125
+ "--all",
1126
+ "--human-readable",
1127
+ "--si",
1128
+ "--inodes",
1129
+ "--local",
1130
+ "--portability",
1131
+ "--print-type",
1132
+ "--total",
1133
+ "--sync",
1134
+ "--no-sync"
1135
+ ],
1136
+ values: ["-B", "-t", "-x", "--block-size", "--type", "--exclude-type"]
1137
+ },
1138
+ file: {
1139
+ cluster: true,
1140
+ // `-C`/`--compile` is absent: it writes a compiled magic file.
1141
+ booleans: [
1142
+ "-b",
1143
+ "-c",
1144
+ "-h",
1145
+ "-i",
1146
+ "-k",
1147
+ "-L",
1148
+ "-n",
1149
+ "-N",
1150
+ "-p",
1151
+ "-r",
1152
+ "-s",
1153
+ "-S",
1154
+ "-v",
1155
+ "-z",
1156
+ "-0",
1157
+ "--brief",
1158
+ "--mime",
1159
+ "--mime-type",
1160
+ "--mime-encoding",
1161
+ "--dereference",
1162
+ "--no-dereference",
1163
+ "--keep-going",
1164
+ "--no-buffer",
1165
+ "--no-pad",
1166
+ "--raw",
1167
+ "--special-files",
1168
+ "--uncompress",
1169
+ "--print0",
1170
+ "--checking-printout"
1171
+ ],
1172
+ values: [
1173
+ "-e",
1174
+ "-m",
1175
+ "-P",
1176
+ "-F",
1177
+ "--exclude",
1178
+ "--magic-file",
1179
+ "--separator",
1180
+ "--parameter",
1181
+ "--extension",
1182
+ "--files-from"
1183
+ ]
1184
+ },
1185
+ head: {
1186
+ cluster: true,
1187
+ booleans: ["-q", "-v", "-z", "--quiet", "--silent", "--verbose", "--zero-terminated"],
1188
+ values: ["-c", "-n", "--bytes", "--lines"],
1189
+ patterns: [COUNT_FLAG]
1190
+ },
1191
+ tail: {
1192
+ cluster: true,
1193
+ booleans: [
1194
+ "-f",
1195
+ "-F",
1196
+ "-q",
1197
+ "-v",
1198
+ "-z",
1199
+ "--follow",
1200
+ "--retry",
1201
+ "--quiet",
1202
+ "--silent",
1203
+ "--verbose",
1204
+ "--zero-terminated"
1205
+ ],
1206
+ values: [
1207
+ "-c",
1208
+ "-n",
1209
+ "-s",
1210
+ "--bytes",
1211
+ "--lines",
1212
+ "--sleep-interval",
1213
+ "--pid",
1214
+ "--max-unchanged-stats"
1215
+ ],
1216
+ patterns: [COUNT_FLAG]
1217
+ },
1218
+ cat: {
1219
+ cluster: true,
1220
+ booleans: [
1221
+ "-A",
1222
+ "-b",
1223
+ "-e",
1224
+ "-E",
1225
+ "-n",
1226
+ "-s",
1227
+ "-t",
1228
+ "-T",
1229
+ "-u",
1230
+ "-v",
1231
+ "--show-all",
1232
+ "--number-nonblank",
1233
+ "--show-ends",
1234
+ "--number",
1235
+ "--squeeze-blank",
1236
+ "--show-tabs",
1237
+ "--show-nonprinting"
1238
+ ]
1239
+ },
1240
+ sort: {
1241
+ cluster: true,
1242
+ // Absent on purpose: `-o`/`--output` writes a file with no redirect for the
1243
+ // redirect check to see, and `--compress-program` names a program that sort
1244
+ // runs over its own temporary files — a permitted binary as an interpreter.
1245
+ booleans: [
1246
+ "-b",
1247
+ "-c",
1248
+ "-C",
1249
+ "-d",
1250
+ "-f",
1251
+ "-g",
1252
+ "-h",
1253
+ "-i",
1254
+ "-M",
1255
+ "-n",
1256
+ "-r",
1257
+ "-R",
1258
+ "-s",
1259
+ "-u",
1260
+ "-V",
1261
+ "-z",
1262
+ "--ignore-leading-blanks",
1263
+ "--check",
1264
+ "--dictionary-order",
1265
+ "--ignore-case",
1266
+ "--general-numeric-sort",
1267
+ "--human-numeric-sort",
1268
+ "--ignore-nonprinting",
1269
+ "--month-sort",
1270
+ "--numeric-sort",
1271
+ "--random-sort",
1272
+ "--reverse",
1273
+ "--stable",
1274
+ "--unique",
1275
+ "--version-sort",
1276
+ "--zero-terminated",
1277
+ "--debug"
1278
+ ],
1279
+ values: [
1280
+ "-k",
1281
+ "-t",
1282
+ "-S",
1283
+ "-T",
1284
+ "--key",
1285
+ "--field-separator",
1286
+ "--buffer-size",
1287
+ "--temporary-directory",
1288
+ "--random-source",
1289
+ "--batch-size",
1290
+ "--parallel",
1291
+ "--sort"
1292
+ ]
1293
+ },
1294
+ uniq: {
1295
+ cluster: true,
1296
+ booleans: [
1297
+ "-c",
1298
+ "-d",
1299
+ "-D",
1300
+ "-i",
1301
+ "-u",
1302
+ "-z",
1303
+ "--count",
1304
+ "--repeated",
1305
+ "--all-repeated",
1306
+ "--ignore-case",
1307
+ "--unique",
1308
+ "--zero-terminated"
1309
+ ],
1310
+ values: ["-f", "-s", "-w", "--skip-fields", "--skip-chars", "--check-chars", "--group"]
1311
+ },
1312
+ // No flag on either runs a program or writes a file, and the planner's
1313
+ // commonest use of `echo` is a section marker — `echo --- files changed ---`
1314
+ // is three tokens that all begin with a dash and none of them is a flag.
1315
+ echo: { freeform: true },
1316
+ printf: { freeform: true },
1317
+ date: {
1318
+ cluster: true,
1319
+ // `-s`/`--set` is absent: it sets the system clock.
1320
+ booleans: [
1321
+ "-u",
1322
+ "-R",
1323
+ "-I",
1324
+ "--utc",
1325
+ "--universal",
1326
+ "--rfc-email",
1327
+ "--rfc-3339",
1328
+ "--iso-8601",
1329
+ "--debug"
1330
+ ],
1331
+ values: ["-d", "-f", "-r", "--date", "--file", "--reference"]
1332
+ },
1333
+ stat: {
1334
+ cluster: true,
1335
+ booleans: ["-f", "-L", "-t", "--file-system", "--terse", "--dereference"],
1336
+ values: ["-c", "--format", "--printf", "--cached"]
1337
+ },
1338
+ basename: {
1339
+ cluster: true,
1340
+ booleans: ["-a", "-z", "--multiple", "--zero"],
1341
+ values: ["-s", "--suffix"]
1342
+ },
1343
+ dirname: {
1344
+ cluster: true,
1345
+ booleans: ["-z", "--zero"]
1346
+ },
1347
+ realpath: {
1348
+ cluster: true,
1349
+ booleans: [
1350
+ "-e",
1351
+ "-m",
1352
+ "-L",
1353
+ "-P",
1354
+ "-q",
1355
+ "-s",
1356
+ "-z",
1357
+ "--canonicalize-existing",
1358
+ "--canonicalize-missing",
1359
+ "--logical",
1360
+ "--physical",
1361
+ "--quiet",
1362
+ "--strip",
1363
+ "--no-symlinks",
1364
+ "--zero"
1365
+ ],
1366
+ values: ["--relative-to", "--relative-base"]
1367
+ },
1368
+ pwd: { cluster: true, booleans: ["-L", "-P", "--logical", "--physical"] },
1369
+ which: { cluster: true, booleans: ["-a", "-s", "--all"] },
1370
+ type: { cluster: true, booleans: ["-a", "-f", "-p", "-P", "-t"] },
1371
+ uname: {
1372
+ cluster: true,
1373
+ booleans: [
1374
+ "-a",
1375
+ "-i",
1376
+ "-m",
1377
+ "-n",
1378
+ "-o",
1379
+ "-p",
1380
+ "-r",
1381
+ "-s",
1382
+ "-v",
1383
+ "--all",
1384
+ "--kernel-name",
1385
+ "--nodename",
1386
+ "--kernel-release",
1387
+ "--kernel-version",
1388
+ "--machine",
1389
+ "--processor",
1390
+ "--hardware-platform",
1391
+ "--operating-system"
1392
+ ]
1393
+ },
1394
+ whoami: {},
1395
+ cut: {
1396
+ cluster: true,
1397
+ // `--output-delimiter` names a separator string, not a file.
1398
+ booleans: ["-n", "-s", "-z", "--only-delimited", "--complement", "--zero-terminated"],
1399
+ values: [
1400
+ "-b",
1401
+ "-c",
1402
+ "-d",
1403
+ "-f",
1404
+ "--bytes",
1405
+ "--characters",
1406
+ "--delimiter",
1407
+ "--fields",
1408
+ "--output-delimiter"
1409
+ ]
1410
+ },
1411
+ nl: {
1412
+ cluster: true,
1413
+ booleans: ["-p", "--no-renumber"],
1414
+ values: [
1415
+ "-b",
1416
+ "-d",
1417
+ "-f",
1418
+ "-h",
1419
+ "-i",
1420
+ "-l",
1421
+ "-n",
1422
+ "-s",
1423
+ "-v",
1424
+ "-w",
1425
+ "--body-numbering",
1426
+ "--section-delimiter",
1427
+ "--footer-numbering",
1428
+ "--header-numbering",
1429
+ "--join-blank-lines",
1430
+ "--line-increment",
1431
+ "--number-format",
1432
+ "--number-separator",
1433
+ "--starting-line-number",
1434
+ "--number-width"
1435
+ ]
1436
+ },
1437
+ rg: {
1438
+ cluster: true,
1439
+ // `--pre` runs a named preprocessor over every file searched and
1440
+ // `--hostname-bin` runs a named program to resolve the hostname; both are
1441
+ // absent, and `--pre-glob` with them.
1442
+ booleans: [
1443
+ "-a",
1444
+ "-c",
1445
+ "-F",
1446
+ "-h",
1447
+ "-H",
1448
+ "-i",
1449
+ "-I",
1450
+ "-l",
1451
+ "-L",
1452
+ "-n",
1453
+ "-N",
1454
+ "-o",
1455
+ "-p",
1456
+ "-P",
1457
+ "-q",
1458
+ "-s",
1459
+ "-S",
1460
+ "-u",
1461
+ "-uu",
1462
+ "-uuu",
1463
+ "-U",
1464
+ "-v",
1465
+ "-V",
1466
+ "-w",
1467
+ "-x",
1468
+ "-z",
1469
+ "--text",
1470
+ "--count",
1471
+ "--count-matches",
1472
+ "--fixed-strings",
1473
+ "--files",
1474
+ "--files-with-matches",
1475
+ "--files-without-match",
1476
+ "--follow",
1477
+ "--hidden",
1478
+ "--no-hidden",
1479
+ "--ignore-case",
1480
+ "--case-sensitive",
1481
+ "--smart-case",
1482
+ "--invert-match",
1483
+ "--line-number",
1484
+ "--no-line-number",
1485
+ "--with-filename",
1486
+ "--no-filename",
1487
+ "--heading",
1488
+ "--no-heading",
1489
+ "--column",
1490
+ "--no-column",
1491
+ "--byte-offset",
1492
+ "--vimgrep",
1493
+ "--json",
1494
+ "--stats",
1495
+ "--trim",
1496
+ "--pretty",
1497
+ "--passthru",
1498
+ "--word-regexp",
1499
+ "--line-regexp",
1500
+ "--multiline",
1501
+ "--multiline-dotall",
1502
+ "--no-multiline",
1503
+ "--crlf",
1504
+ "--null",
1505
+ "--null-data",
1506
+ "--binary",
1507
+ "--search-zip",
1508
+ "--no-ignore",
1509
+ "--no-ignore-dot",
1510
+ "--no-ignore-exclude",
1511
+ "--no-ignore-files",
1512
+ "--no-ignore-global",
1513
+ "--no-ignore-parent",
1514
+ "--no-ignore-vcs",
1515
+ "--no-ignore-messages",
1516
+ "--no-messages",
1517
+ "--no-require-git",
1518
+ "--require-git",
1519
+ "--one-file-system",
1520
+ "--pcre2",
1521
+ "--no-pcre2",
1522
+ "--auto-hybrid-regex",
1523
+ "--line-buffered",
1524
+ "--block-buffered",
1525
+ "--unicode",
1526
+ "--no-unicode",
1527
+ "--unrestricted",
1528
+ "--type-list",
1529
+ "--debug",
1530
+ "--trace",
1531
+ "--include-zero",
1532
+ "--no-context-separator",
1533
+ "--stop-on-nonmatch",
1534
+ "--quiet",
1535
+ "--no-binary"
1536
+ ],
1537
+ values: [
1538
+ "-A",
1539
+ "-B",
1540
+ "-C",
1541
+ "-d",
1542
+ "-e",
1543
+ "-E",
1544
+ "-f",
1545
+ "-g",
1546
+ "-j",
1547
+ "-m",
1548
+ "-M",
1549
+ "-r",
1550
+ "-t",
1551
+ "-T",
1552
+ "--after-context",
1553
+ "--before-context",
1554
+ "--context",
1555
+ "--context-separator",
1556
+ "--field-context-separator",
1557
+ "--field-match-separator",
1558
+ "--regexp",
1559
+ "--file",
1560
+ "--glob",
1561
+ "--iglob",
1562
+ "--glob-case-insensitive",
1563
+ "--type",
1564
+ "--type-not",
1565
+ "--type-add",
1566
+ "--type-clear",
1567
+ "--max-count",
1568
+ "--max-columns",
1569
+ "--max-depth",
1570
+ "--maxdepth",
1571
+ "--max-filesize",
1572
+ "--replace",
1573
+ "--threads",
1574
+ "--encoding",
1575
+ "--engine",
1576
+ "--sort",
1577
+ "--sortr",
1578
+ "--color",
1579
+ "--colors",
1580
+ "--path-separator",
1581
+ "--ignore-file",
1582
+ "--dfa-size-limit",
1583
+ "--regex-size-limit",
1584
+ "--binary-files",
1585
+ "--max-columns-preview"
1586
+ ]
1587
+ },
1588
+ grep: {
1589
+ cluster: true,
1590
+ // `-o` here is `--only-matching`, which writes nothing. It is the same two
1591
+ // characters as `sort -o`, which writes a file — the reason these sets are
1592
+ // per-binary rather than one shared list.
1593
+ booleans: [
1594
+ "-a",
1595
+ "-b",
1596
+ "-c",
1597
+ "-E",
1598
+ "-F",
1599
+ "-G",
1600
+ "-H",
1601
+ "-h",
1602
+ "-i",
1603
+ "-I",
1604
+ "-l",
1605
+ "-L",
1606
+ "-n",
1607
+ "-o",
1608
+ "-P",
1609
+ "-q",
1610
+ "-r",
1611
+ "-R",
1612
+ "-s",
1613
+ "-T",
1614
+ "-U",
1615
+ "-v",
1616
+ "-w",
1617
+ "-x",
1618
+ "-y",
1619
+ "-z",
1620
+ "-Z",
1621
+ "--extended-regexp",
1622
+ "--fixed-strings",
1623
+ "--basic-regexp",
1624
+ "--perl-regexp",
1625
+ "--ignore-case",
1626
+ "--no-ignore-case",
1627
+ "--invert-match",
1628
+ "--word-regexp",
1629
+ "--line-regexp",
1630
+ "--count",
1631
+ "--files-with-matches",
1632
+ "--files-without-match",
1633
+ "--line-number",
1634
+ "--with-filename",
1635
+ "--no-filename",
1636
+ "--only-matching",
1637
+ "--quiet",
1638
+ "--silent",
1639
+ "--no-messages",
1640
+ "--text",
1641
+ "--binary",
1642
+ "--line-buffered",
1643
+ "--null",
1644
+ "--null-data",
1645
+ "--byte-offset",
1646
+ "--recursive",
1647
+ "--dereference-recursive",
1648
+ "--initial-tab",
1649
+ "--unix-byte-offsets",
1650
+ "--no-group-separator",
1651
+ "--color",
1652
+ "--colour"
1653
+ ],
1654
+ values: [
1655
+ "-A",
1656
+ "-B",
1657
+ "-C",
1658
+ "-d",
1659
+ "-D",
1660
+ "-e",
1661
+ "-f",
1662
+ "-m",
1663
+ "--after-context",
1664
+ "--before-context",
1665
+ "--context",
1666
+ "--regexp",
1667
+ "--file",
1668
+ "--max-count",
1669
+ "--devices",
1670
+ "--directories",
1671
+ "--binary-files",
1672
+ "--include",
1673
+ "--exclude",
1674
+ "--exclude-dir",
1675
+ "--exclude-from",
1676
+ "--label",
1677
+ "--group-separator"
1678
+ ]
1679
+ },
1680
+ find: {
1681
+ // No clustering: find spells its flags as single-dash words, so walking the
1682
+ // characters of `-name` would read it as five short flags. And its arguments
1683
+ // are an expression, so `--` does not end them.
1684
+ expressionArgs: true,
1685
+ // Absent on purpose: `-exec`, `-execdir`, `-ok` and `-okdir` run an inner
1686
+ // command (also caught by the named guard below, which says so more
1687
+ // precisely), `-delete` removes files, and `-fprint`, `-fprint0`, `-fprintf`
1688
+ // and `-fls` are find's write family — the same write as `> file` with no
1689
+ // redirect for the redirect check to see.
1690
+ booleans: [
1691
+ "-H",
1692
+ "-L",
1693
+ "-P",
1694
+ "-a",
1695
+ "-and",
1696
+ "-o",
1697
+ "-or",
1698
+ "-not",
1699
+ "-d",
1700
+ "-depth",
1701
+ "-daystart",
1702
+ "-empty",
1703
+ "-executable",
1704
+ "-false",
1705
+ "-follow",
1706
+ "-help",
1707
+ "--help",
1708
+ "-ignore_readdir_race",
1709
+ "-noignore_readdir_race",
1710
+ "-ls",
1711
+ "-mount",
1712
+ "-nogroup",
1713
+ "-noleaf",
1714
+ "-nouser",
1715
+ "-print",
1716
+ "-print0",
1717
+ "-prune",
1718
+ "-quit",
1719
+ "-readable",
1720
+ "-true",
1721
+ "-version",
1722
+ "-writable",
1723
+ "-xdev"
1724
+ ],
1725
+ values: [
1726
+ "-maxdepth",
1727
+ "-mindepth",
1728
+ "-name",
1729
+ "-iname",
1730
+ "-path",
1731
+ "-ipath",
1732
+ "-wholename",
1733
+ "-iwholename",
1734
+ "-regex",
1735
+ "-iregex",
1736
+ "-lname",
1737
+ "-ilname",
1738
+ "-type",
1739
+ "-xtype",
1740
+ "-size",
1741
+ "-newer",
1742
+ "-anewer",
1743
+ "-cnewer",
1744
+ "-mtime",
1745
+ "-mmin",
1746
+ "-ctime",
1747
+ "-cmin",
1748
+ "-atime",
1749
+ "-amin",
1750
+ "-used",
1751
+ "-uid",
1752
+ "-gid",
1753
+ "-user",
1754
+ "-group",
1755
+ "-perm",
1756
+ "-links",
1757
+ "-inum",
1758
+ "-samefile",
1759
+ "-fstype",
1760
+ "-regextype",
1761
+ "-files0-from",
1762
+ "-printf",
1763
+ "-context",
1764
+ "-D"
1765
+ ],
1766
+ // `find -O2` glues the optimisation level on, and `-newermt`/`-newerat` and
1767
+ // friends spell the two comparison fields into the flag name.
1768
+ patterns: [/^-O\d$/, /^-newer[acmBt][acmBt]$/]
1769
+ },
1770
+ jq: {
1771
+ cluster: true,
1772
+ booleans: [
1773
+ "-a",
1774
+ "-c",
1775
+ "-C",
1776
+ "-e",
1777
+ "-h",
1778
+ "-j",
1779
+ "-M",
1780
+ "-n",
1781
+ "-r",
1782
+ "-R",
1783
+ "-s",
1784
+ "-S",
1785
+ "--ascii-output",
1786
+ "--compact-output",
1787
+ "--color-output",
1788
+ "--monochrome-output",
1789
+ "--exit-status",
1790
+ "--join-output",
1791
+ "--null-input",
1792
+ "--raw-input",
1793
+ "--raw-output",
1794
+ "--raw-output0",
1795
+ "--slurp",
1796
+ "--sort-keys",
1797
+ "--seq",
1798
+ "--stream",
1799
+ "--stream-errors",
1800
+ "--tab",
1801
+ "--unbuffered",
1802
+ "--args",
1803
+ "--jsonargs"
1804
+ ],
1805
+ values: [
1806
+ "-f",
1807
+ "-L",
1808
+ "--arg",
1809
+ "--argjson",
1810
+ "--slurpfile",
1811
+ "--rawfile",
1812
+ "--indent",
1813
+ "--from-file"
1814
+ ]
1815
+ },
1816
+ yq: {
1817
+ cluster: true,
1818
+ // `-i`/`--inplace`/`--in-place` rewrite the file, and `-s`/`--split-exp`
1819
+ // writes one file per document. All absent — and all bare or near-bare
1820
+ // booleans, which is why exempting booleans was not an option.
1821
+ booleans: [
1822
+ "-C",
1823
+ "-e",
1824
+ "-h",
1825
+ "-j",
1826
+ "-M",
1827
+ "-n",
1828
+ "-N",
1829
+ "-P",
1830
+ "-r",
1831
+ "-S",
1832
+ "-v",
1833
+ "-y",
1834
+ "-Y",
1835
+ "--colors",
1836
+ "--no-colors",
1837
+ "--exit-status",
1838
+ "--no-doc",
1839
+ "--null-input",
1840
+ "--prettyPrint",
1841
+ "--raw-output",
1842
+ "--sort-keys",
1843
+ "--tojson",
1844
+ "--yaml-output",
1845
+ "--yaml-roundtrip",
1846
+ "--unwrapScalar",
1847
+ "--header-preprocess",
1848
+ "--no-exit-status",
1849
+ "--verbose",
1850
+ "--xml-strict-mode"
1851
+ ],
1852
+ values: [
1853
+ "-I",
1854
+ "-o",
1855
+ "-p",
1856
+ "-L",
1857
+ "--indent",
1858
+ "--output-format",
1859
+ "--input-format",
1860
+ "--expression",
1861
+ "--width",
1862
+ "--xml-attribute-prefix",
1863
+ "--xml-content-name"
1864
+ ]
1865
+ }
1866
+ };
645
1867
  var GIT_READONLY_SUBCOMMANDS = [
646
1868
  "log",
647
1869
  "status",
648
1870
  "diff",
649
1871
  "show",
650
1872
  "ls-files",
651
- "ls-remote",
652
1873
  "ls-tree",
653
1874
  "branch",
654
1875
  "tag",
@@ -763,6 +1984,26 @@ var REFUSED_SUBCOMMANDS = {
763
1984
  az: ["login", "logout"],
764
1985
  terraform: ["apply", "destroy"]
765
1986
  };
1987
+ var SHELL_KEYWORDS = [
1988
+ "{",
1989
+ "}",
1990
+ "if",
1991
+ "then",
1992
+ "elif",
1993
+ "else",
1994
+ "fi",
1995
+ "for",
1996
+ "while",
1997
+ "until",
1998
+ "do",
1999
+ "done",
2000
+ "case",
2001
+ "esac",
2002
+ "select",
2003
+ "function",
2004
+ "time",
2005
+ "export"
2006
+ ];
766
2007
  var INTERPRETERS = [
767
2008
  "sh",
768
2009
  "bash",
@@ -1206,16 +2447,79 @@ function pathLikeArgs(command, opts = {}) {
1206
2447
  return !a.startsWith("-") && looksLikePath(a) ? [a] : [];
1207
2448
  }));
1208
2449
  }
2450
+ function matchFlag(spec, booleans, values, token) {
2451
+ if (token.startsWith("--")) {
2452
+ const eq = token.indexOf("=");
2453
+ const name = eq > 0 ? token.slice(0, eq) : token;
2454
+ if (booleans.has(name)) return "boolean";
2455
+ if (values.has(name)) return eq > 0 ? "boolean" : "value";
2456
+ return void 0;
2457
+ }
2458
+ if (booleans.has(token)) return "boolean";
2459
+ if (values.has(token)) return "value";
2460
+ if (spec.patterns?.some((p) => p.test(token))) return "boolean";
2461
+ if (!spec.cluster) return void 0;
2462
+ for (let i = 1; i < token.length; i++) {
2463
+ const short = `-${token[i]}`;
2464
+ if (booleans.has(short)) continue;
2465
+ if (values.has(short)) return i === token.length - 1 ? "value" : "boolean";
2466
+ return void 0;
2467
+ }
2468
+ return "boolean";
2469
+ }
2470
+ function takesNextToken(next) {
2471
+ if (next === void 0) return false;
2472
+ return !/^-./.test(next) || /^-\d/.test(next);
2473
+ }
2474
+ function flagLabel(token) {
2475
+ const eq = token.startsWith("--") ? token.indexOf("=") : -1;
2476
+ return eq > 0 ? token.slice(0, eq) : token;
2477
+ }
2478
+ function scanFlags(seg) {
2479
+ const spec = FLAG_POLICY[seg.binary];
2480
+ if (!spec || spec.freeform) return void 0;
2481
+ const booleans = /* @__PURE__ */ new Set([...spec.booleans ?? [], ...UNIVERSAL_FLAGS]);
2482
+ const values = new Set(spec.values ?? []);
2483
+ const operands = [];
2484
+ let i = 0;
2485
+ while (i < seg.args.length) {
2486
+ const token = seg.args[i];
2487
+ if (token === "--" && !spec.expressionArgs) {
2488
+ operands.push(...seg.args.slice(i + 1));
2489
+ break;
2490
+ }
2491
+ if (!/^-./.test(token)) {
2492
+ operands.push(token);
2493
+ i++;
2494
+ continue;
2495
+ }
2496
+ const shape = matchFlag(spec, booleans, values, token);
2497
+ if (shape === void 0) return { unknown: flagLabel(token), operands };
2498
+ i += shape === "value" && takesNextToken(seg.args[i + 1]) ? 2 : 1;
2499
+ }
2500
+ return { operands };
2501
+ }
2502
+ function subcommandOf(seg) {
2503
+ const scan = scanFlags(seg);
2504
+ if (scan) return scan.operands[0];
2505
+ return seg.args.find((a) => !a.startsWith("-"));
2506
+ }
2507
+ var SCOPE_LEAD_ARGS = 2;
1209
2508
  function scopeFor(seg) {
1210
2509
  if (!MULTIPLEXERS.includes(seg.binary)) return seg.binary;
1211
- const sub = seg.args.find((a) => !a.startsWith("-"));
1212
- return sub ? `${seg.binary} ${sub}` : seg.binary;
2510
+ const lead = [];
2511
+ for (const arg of seg.args) {
2512
+ if (arg.startsWith("-")) break;
2513
+ lead.push(arg);
2514
+ if (lead.length === SCOPE_LEAD_ARGS) break;
2515
+ }
2516
+ return [seg.binary, ...lead].join(" ");
1213
2517
  }
1214
2518
  function isAuto(seg) {
1215
2519
  if (!AUTO_COMMANDS.includes(seg.binary)) return false;
1216
2520
  if (seg.expandable) return false;
1217
2521
  if (seg.binary !== "git") return true;
1218
- const sub = seg.args.find((a) => !a.startsWith("-"));
2522
+ const sub = subcommandOf(seg);
1219
2523
  return sub !== void 0 && GIT_READONLY_SUBCOMMANDS.includes(sub);
1220
2524
  }
1221
2525
  function refusalFor(seg) {
@@ -1225,29 +2529,31 @@ function refusalFor(seg) {
1225
2529
  if (seg.binary === "eval" || seg.binary === "exec") {
1226
2530
  return `"${seg.binary}" runs a string as a command, which this classifier cannot inspect. Use the read-only research tools, or describe it as a task.`;
1227
2531
  }
2532
+ if (seg.binary === "source" || seg.binary === ".") {
2533
+ return `"${seg.binary}" runs a file's contents as commands, which this classifier cannot inspect. Use the read-only research tools, or describe it as a task.`;
2534
+ }
2535
+ if (SHELL_KEYWORDS.includes(seg.binary)) {
2536
+ return `"${seg.binary}" is a shell keyword or compound-command opener. This classifier only inspects the command that runs first, and a keyword hides the real one from it. Run the inner command directly, or describe it as a task.`;
2537
+ }
1228
2538
  const subs = REFUSED_SUBCOMMANDS[seg.binary];
1229
2539
  if (subs) {
1230
- const sub = seg.args.find((a) => !a.startsWith("-"));
2540
+ const sub = subcommandOf(seg);
1231
2541
  if (sub && subs.includes(sub)) {
1232
2542
  return `"${seg.binary} ${sub}" changes state or reaches outward. You are a read-only planner \u2014 put it in the plan and let the runner do it.`;
1233
2543
  }
1234
2544
  }
1235
2545
  if (seg.binary === "git") {
1236
- const sub = seg.args.find((a) => !a.startsWith("-"));
2546
+ const sub = subcommandOf(seg);
1237
2547
  if (sub === "branch" || sub === "tag") {
1238
2548
  const flag = seg.args.find((a) => ["-D", "-d", "-m", "-M", "--delete", "--move"].includes(a));
1239
2549
  if (flag) {
1240
2550
  return `"git ${sub} ${flag}" mutates refs. You are a read-only planner \u2014 describe the change as a task instead.`;
1241
2551
  }
1242
- }
1243
- const execPathFlag = seg.args.find((a) => a === "--exec-path" || a.startsWith("--exec-path="));
1244
- if (execPathFlag) {
1245
- return `"git ${execPathFlag}" redirects where git looks for its helper programs, and can make it run an arbitrary one. Use the read-only research tools, or describe the change as a task.`;
1246
- }
1247
- if (sub === "grep") {
1248
- const pagerFlag = seg.args.find((a) => a === "-O" || a === "--open-files-in-pager" || a.startsWith("--open-files-in-pager="));
1249
- if (pagerFlag) {
1250
- return `"git grep ${pagerFlag}" opens the results in an arbitrary named program. Use the read-only research tools, or describe the change as a task.`;
2552
+ const listing = seg.args.includes("-l") || seg.args.includes("--list");
2553
+ const scan2 = scanFlags(seg);
2554
+ const target = scan2?.operands[1];
2555
+ if (!listing && target) {
2556
+ return `"git ${sub} ${target}" creates or moves a ref. You are a read-only planner \u2014 describe the change as a task instead.`;
1251
2557
  }
1252
2558
  }
1253
2559
  }
@@ -1275,6 +2581,13 @@ function refusalFor(seg) {
1275
2581
  if (seg.assignments.length > 0) {
1276
2582
  return `The environment assignment "${seg.assignments[0]}" decides what "${seg.binary}" actually does, and this classifier cannot judge the value. Re-run the command without the assignment.`;
1277
2583
  }
2584
+ const scan = scanFlags(seg);
2585
+ if (scan?.unknown) {
2586
+ return `"${scan.unknown}" is not a flag this classifier knows to be read-only on "${seg.binary}", and flags on otherwise read-only binaries can run a helper program or write a file. Re-run with read-only flags only, or describe the work as a task.`;
2587
+ }
2588
+ if (seg.binary === "uniq" && scan && scan.operands.length > 1) {
2589
+ return `"uniq" writes to its second file argument ("${scan.operands[1]}"). You are a read-only planner \u2014 pipe the output instead of naming an output file, or describe the write as a task.`;
2590
+ }
1278
2591
  return void 0;
1279
2592
  }
1280
2593
  function withWrapperNote(reason, wrappers) {
@@ -1661,6 +2974,139 @@ It is outside the auto-allowed read-only set (${scope}). Continue with the read-
1661
2974
  }
1662
2975
  };
1663
2976
 
2977
+ // src/utils/redactSecrets.ts
2978
+ var REDACTION_MARKER = "REDACTED";
2979
+ function marker(kind, length) {
2980
+ return `[${REDACTION_MARKER} ${kind}: ${length} chars]`;
2981
+ }
2982
+ var LINE_PREFIX = String.raw`(?:[ \t]*\d+\||[^\s:]*:\d+:|)[ \t]*`;
2983
+ var LINE_PREFIX_ANCHORED = new RegExp("^" + LINE_PREFIX);
2984
+ var CREDENTIAL_WORD = String.raw`(?:api[_-]?keys?|secrets?|passwords?|passwd|passphrase|tokens?|access[_-]?keys?|private[_-]?keys?|client[_-]?secrets?|credentials?|auth)(?![a-z])`;
2985
+ var NAMED_CREDENTIAL = new RegExp(
2986
+ String.raw`((?:^|[^A-Za-z0-9_])["']?[A-Za-z0-9_.\-]*` + CREDENTIAL_WORD + String.raw`[A-Za-z0-9_.\-]*["']?[ \t]*(?:=>|[:=])[ \t]*)(["'\x60]?)([A-Za-z0-9_\-./+=:~!@#%^&*]{8,})\2`,
2987
+ "gi"
2988
+ );
2989
+ var DOTENV_CREDENTIAL = new RegExp(
2990
+ String.raw`^(` + LINE_PREFIX + String.raw`(?:export[ \t]+)?[A-Z0-9_]*` + CREDENTIAL_WORD.toUpperCase() + String.raw`[A-Z0-9_]*[ \t]*=[ \t]*)(["'\x60]?)([^\s"'\x60]{3,})\2[ \t]*$`,
2991
+ "gm"
2992
+ );
2993
+ var PROVIDER_KEY_PATTERNS = [
2994
+ // OpenAI / Anthropic / OpenRouter and the many `sk-` derivatives
2995
+ /(?<![A-Za-z0-9])sk-(?:ant-|proj-|or-v1-|live-|test-)?[A-Za-z0-9_-]{20,}/g,
2996
+ // GitHub personal-access, OAuth, app and refresh tokens
2997
+ /(?<![A-Za-z0-9])(?:gh[pousr]|github_pat)_[A-Za-z0-9_]{22,}/g,
2998
+ // Slack bot / user / app tokens
2999
+ /(?<![A-Za-z0-9])xox[abprse]-[A-Za-z0-9-]{10,}/g,
3000
+ // Stripe secret and restricted keys
3001
+ /(?<![A-Za-z0-9])[sr]k_(?:live|test)_[A-Za-z0-9]{16,}/g,
3002
+ // Google API keys and OAuth access tokens
3003
+ /(?<![A-Za-z0-9])AIza[A-Za-z0-9_-]{30,}/g,
3004
+ /(?<![A-Za-z0-9])ya29\.[A-Za-z0-9_-]{20,}/g,
3005
+ // npm, Hugging Face, Groq, GitLab, DigitalOcean, Shopify
3006
+ /(?<![A-Za-z0-9])(?:npm|hf|gsk|glpat|dop_v1|shpat|shpss)[_-][A-Za-z0-9]{20,}/g,
3007
+ /(?<![A-Za-z0-9])xai-[A-Za-z0-9]{20,}/g,
3008
+ // Three-segment JWTs, whose payload segment is base64 for `{"`
3009
+ /(?<![A-Za-z0-9])eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
3010
+ ];
3011
+ var CLOUD_ACCESS_KEY_ID = /(?<![A-Za-z0-9])(?:A3T[A-Z0-9]{2}|AKIA|ASIA|ABIA|ACCA|AGPA|AIDA|AIPA|ANPA|ANVA|AROA)[A-Z0-9]{16}(?![A-Za-z0-9])/g;
3012
+ var PEM_BEGIN = /-----BEGIN ((?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?)-----/;
3013
+ var PEM_BODY_LINE = /^[A-Za-z0-9+/=:,.\- \t]*$/;
3014
+ function redactPrivateKeyBlocks(text) {
3015
+ if (!PEM_BEGIN.test(text)) return text;
3016
+ const out = [];
3017
+ let label = null;
3018
+ let bodyChars = 0;
3019
+ const closeBlock = () => {
3020
+ if (label === null) return;
3021
+ out.push(marker("private key", bodyChars));
3022
+ label = null;
3023
+ bodyChars = 0;
3024
+ };
3025
+ for (const line of text.split("\n")) {
3026
+ if (label === null) {
3027
+ out.push(line);
3028
+ const begun = PEM_BEGIN.exec(line);
3029
+ if (begun) label = begun[1];
3030
+ continue;
3031
+ }
3032
+ const content = line.replace(LINE_PREFIX_ANCHORED, "");
3033
+ if (content.includes(`-----END ${label}-----`)) {
3034
+ closeBlock();
3035
+ out.push(line);
3036
+ } else if (PEM_BODY_LINE.test(content)) {
3037
+ bodyChars += content.trim().length;
3038
+ } else {
3039
+ closeBlock();
3040
+ out.push(line);
3041
+ }
3042
+ }
3043
+ closeBlock();
3044
+ return out.join("\n");
3045
+ }
3046
+ var NUMERIC = /^[-+]?\d+(?:\.\d+)?$/;
3047
+ var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
3048
+ var MEMBER_EXPRESSION = /^[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)+$/;
3049
+ var SLUG = /^[a-z]+(?:[-_][a-z]+)+$/;
3050
+ var PATHISH = /^(?:\.{0,2}\/|~\/)/;
3051
+ var URLISH = /^[a-z][a-z0-9+.-]*:\/\//i;
3052
+ var CODE_KEYWORDS = /* @__PURE__ */ new Set([
3053
+ "true",
3054
+ "false",
3055
+ "null",
3056
+ "none",
3057
+ "nil",
3058
+ "undefined",
3059
+ "void",
3060
+ "string",
3061
+ "number",
3062
+ "boolean",
3063
+ "object",
3064
+ "any",
3065
+ "unknown",
3066
+ "bool",
3067
+ "str",
3068
+ "int"
3069
+ ]);
3070
+ var UNAMBIGUOUS_CREDENTIAL_NAME = /(?:password|passwd|passphrase|secret|credentials?|private[_-]?key)/i;
3071
+ function looksLikeKeyMaterial(value) {
3072
+ const hasDigit = /\d/.test(value);
3073
+ const hasAlpha = /[A-Za-z]/.test(value);
3074
+ const mixedCase = /[a-z]/.test(value) && /[A-Z]/.test(value);
3075
+ return hasDigit && hasAlpha || mixedCase || value.length >= 24;
3076
+ }
3077
+ function looksLikeCode(value) {
3078
+ return NUMERIC.test(value) || CODE_KEYWORDS.has(value.toLowerCase()) || MEMBER_EXPRESSION.test(value) || PATHISH.test(value) || URLISH.test(value);
3079
+ }
3080
+ function trimTrailingPunctuation(value) {
3081
+ const match = /[.:!]+$/.exec(value);
3082
+ if (!match || match.index === 0) return { value, tail: "" };
3083
+ return { value: value.slice(0, match.index), tail: match[0] };
3084
+ }
3085
+ function redactSecrets(text) {
3086
+ if (!text) return text;
3087
+ let out = redactPrivateKeyBlocks(text);
3088
+ out = out.replace(DOTENV_CREDENTIAL, (full, head, quote, value) => {
3089
+ if (looksLikeCode(value) || value.startsWith("$")) return full;
3090
+ return `${head}${quote}${marker("credential", value.length)}${quote}`;
3091
+ });
3092
+ out = out.replace(NAMED_CREDENTIAL, (full, head, quote, raw) => {
3093
+ const { value, tail } = trimTrailingPunctuation(raw);
3094
+ if (value.length < 8) return full;
3095
+ if (looksLikeCode(value) || value.startsWith("$")) return full;
3096
+ if (UNAMBIGUOUS_CREDENTIAL_NAME.test(head) && /\d/.test(value)) {
3097
+ return `${head}${quote}${marker("credential", value.length)}${quote}${tail}`;
3098
+ }
3099
+ if (IDENTIFIER.test(value) || SLUG.test(value)) return full;
3100
+ if (!UNAMBIGUOUS_CREDENTIAL_NAME.test(head) && !looksLikeKeyMaterial(value)) return full;
3101
+ return `${head}${quote}${marker("credential", value.length)}${quote}${tail}`;
3102
+ });
3103
+ for (const pattern of PROVIDER_KEY_PATTERNS) {
3104
+ out = out.replace(pattern, (hit) => marker("api key", hit.length));
3105
+ }
3106
+ out = out.replace(CLOUD_ACCESS_KEY_ID, (hit) => marker("access key id", hit.length));
3107
+ return out;
3108
+ }
3109
+
1664
3110
  // src/services/executeTool.ts
1665
3111
  var GREP_OUTPUT_MODES = ["content", "files", "count"];
1666
3112
  function num(value) {
@@ -1687,6 +3133,10 @@ function grepOptions(args) {
1687
3133
  }
1688
3134
  var STOPPED_TOOL_RESULT = "Stopped by the user \u2014 this call was not executed, and no further tool calls will run this turn.";
1689
3135
  async function executeTool(name, args, fs10, fetcher, signal) {
3136
+ const outcome = await runTool(name, args, fs10, fetcher, signal);
3137
+ return { ...outcome, output: redactSecrets(outcome.output) };
3138
+ }
3139
+ async function runTool(name, args, fs10, fetcher, signal) {
1690
3140
  if (signal?.aborted) return { success: false, output: STOPPED_TOOL_RESULT, truncated: false };
1691
3141
  switch (name) {
1692
3142
  case "read_file": {
@@ -5955,11 +7405,11 @@ function composeAugmentedPrompt(task, allTasks, opts) {
5955
7405
  if (isHitlTask(task)) {
5956
7406
  blocks.push(renderCheckpointInstruction());
5957
7407
  }
5958
- const marker = renderCompletionMarker(task);
5959
- if (blocks.length === 0) return basePrompt + marker;
7408
+ const marker2 = renderCompletionMarker(task);
7409
+ if (blocks.length === 0) return basePrompt + marker2;
5960
7410
  return `${blocks.join("\n\n")}
5961
7411
 
5962
- ${basePrompt}${marker}`;
7412
+ ${basePrompt}${marker2}`;
5963
7413
  }
5964
7414
 
5965
7415
  // src/services/VerdictEngine.ts
@@ -8906,7 +10356,7 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
8906
10356
  throw new WorkspaceNotAProjectError(workspace);
8907
10357
  }
8908
10358
  const hasMarker = [...PROJECT_MARKER_DIRS, ...PROJECT_MARKER_FILES].some(
8909
- (marker) => exists(path9.join(resolved, marker))
10359
+ (marker2) => exists(path9.join(resolved, marker2))
8910
10360
  );
8911
10361
  if (!hasMarker) throw new WorkspaceNotAProjectError(workspace);
8912
10362
  }
@@ -10126,7 +11576,8 @@ var CliAgentAiService = class {
10126
11576
  let stepIndex = 0;
10127
11577
  const truncate = (value) => value.length > LOG_MAX_CHARS ? `${value.slice(0, LOG_MAX_CHARS)}
10128
11578
  [... truncated, total ${value.length} chars]` : value;
10129
- const settle = (id, output, success, outcome) => {
11579
+ const settle = (id, rawOutput, success, outcome) => {
11580
+ const output = redactSecrets(rawOutput);
10130
11581
  const call = pendingCalls.get(id);
10131
11582
  pendingCalls.delete(id);
10132
11583
  const step = {
@@ -10657,6 +12108,16 @@ function ensureDir(dir) {
10657
12108
  function getStateDir(baseDir) {
10658
12109
  return path10.join(baseDir ?? process.cwd(), STATE_DIR);
10659
12110
  }
12111
+ var IGNORE_FILE = ".gitignore";
12112
+ function ensureStateDirIgnored(baseDir) {
12113
+ const dir = getStateDir(baseDir);
12114
+ ensureDir(dir);
12115
+ try {
12116
+ fs4.writeFileSync(path10.join(dir, IGNORE_FILE), "*\n", { flag: "wx" });
12117
+ } catch (err) {
12118
+ if (err.code !== "EEXIST") throw err;
12119
+ }
12120
+ }
10660
12121
 
10661
12122
  // src/utils/sessionId.ts
10662
12123
  var import_crypto = require("crypto");
@@ -10686,6 +12147,7 @@ function sessionFilename(session) {
10686
12147
  function saveSession(plan, goal, baseDir, id) {
10687
12148
  const sessionsDir = getSessionsDir(baseDir);
10688
12149
  ensureDir(sessionsDir);
12150
+ ensureStateDirIgnored(baseDir);
10689
12151
  const now = (/* @__PURE__ */ new Date()).toISOString();
10690
12152
  const meta = {
10691
12153
  id: id || mintSessionId(),
@@ -13765,6 +15227,7 @@ var PlannerModelMemory = class {
13765
15227
  emptyWarnings,
13766
15228
  enabledRunners,
13767
15229
  ensureDir,
15230
+ ensureStateDirIgnored,
13768
15231
  escapeControlCharsInStrings,
13769
15232
  executionLogBlock,
13770
15233
  executionSummary,