nexrall-code 0.5.126 → 0.5.127

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 (2) hide show
  1. package/dist/index.js +728 -35
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -105415,6 +105415,490 @@ var require_modePolicy = __commonJS({
105415
105415
  }
105416
105416
  });
105417
105417
 
105418
+ // ../../../../../Users/henry/Documents/nexrall-code/packages/core/dist/permissions/destructiveTokens.js
105419
+ var require_destructiveTokens = __commonJS({
105420
+ "../../../../../Users/henry/Documents/nexrall-code/packages/core/dist/permissions/destructiveTokens.js"(exports2) {
105421
+ "use strict";
105422
+ Object.defineProperty(exports2, "__esModule", { value: true });
105423
+ exports2.tokenize = tokenize3;
105424
+ exports2.classifyTokens = classifyTokens;
105425
+ var hit = (category, reason) => ({ category, reason });
105426
+ function tokenize3(src) {
105427
+ const cmds = [];
105428
+ let cur = [];
105429
+ let word = "";
105430
+ let has2 = false;
105431
+ let q = null;
105432
+ const pushWord = () => {
105433
+ if (has2)
105434
+ cur.push(word);
105435
+ word = "";
105436
+ has2 = false;
105437
+ };
105438
+ const pushCmd = () => {
105439
+ pushWord();
105440
+ if (cur.length)
105441
+ cmds.push(cur);
105442
+ cur = [];
105443
+ };
105444
+ for (let i2 = 0; i2 < src.length; i2++) {
105445
+ const ch = src[i2];
105446
+ if (q === "'") {
105447
+ if (ch === "'")
105448
+ q = null;
105449
+ else
105450
+ word += ch;
105451
+ continue;
105452
+ }
105453
+ if (q === '"') {
105454
+ if (ch === "\\" && i2 + 1 < src.length) {
105455
+ word += src[++i2];
105456
+ continue;
105457
+ }
105458
+ if (ch === '"') {
105459
+ q = null;
105460
+ continue;
105461
+ }
105462
+ word += ch;
105463
+ continue;
105464
+ }
105465
+ if (ch === "'" || ch === '"') {
105466
+ q = ch;
105467
+ has2 = true;
105468
+ continue;
105469
+ }
105470
+ if (ch === "\\") {
105471
+ if (i2 + 1 < src.length) {
105472
+ word += src[++i2];
105473
+ has2 = true;
105474
+ }
105475
+ continue;
105476
+ }
105477
+ if (ch === ";" || ch === "&" || ch === "|" || ch === "\n" || ch === "(" || ch === ")" || ch === "`") {
105478
+ pushCmd();
105479
+ continue;
105480
+ }
105481
+ if (ch === " " || ch === " ") {
105482
+ pushWord();
105483
+ continue;
105484
+ }
105485
+ word += ch;
105486
+ has2 = true;
105487
+ }
105488
+ pushCmd();
105489
+ return cmds;
105490
+ }
105491
+ var ENV_ASSIGN = /^[A-Za-z_][A-Za-z0-9_]*=/;
105492
+ var CONTROL_WORDS = /* @__PURE__ */ new Set(["{", "}", "!", "if", "then", "else", "elif", "do", "while", "until", "fi", "done"]);
105493
+ var SHELLS = /* @__PURE__ */ new Set(["bash", "sh", "zsh", "dash", "ksh"]);
105494
+ var base = (w) => {
105495
+ const b = w.includes("/") ? w.slice(w.lastIndexOf("/") + 1) : w;
105496
+ return b;
105497
+ };
105498
+ function dropOpts(args, withArg = /* @__PURE__ */ new Set()) {
105499
+ let i2 = 0;
105500
+ while (i2 < args.length && args[i2].startsWith("-") && args[i2] !== "--") {
105501
+ i2 += withArg.has(args[i2]) ? 2 : 1;
105502
+ }
105503
+ if (args[i2] === "--")
105504
+ i2++;
105505
+ return args.slice(i2);
105506
+ }
105507
+ function has(args, long, short) {
105508
+ return args.some((a) => long !== null && (a === long || a.startsWith(long + "=")) || short !== null && /^-[A-Za-z]+$/.test(a) && a.includes(short));
105509
+ }
105510
+ var BROAD = /* @__PURE__ */ new Set([".", "./", "*", "./*", ":/", ":/", "..", "../", "/", "~", "~/", "$HOME", "${HOME}", ".*"]);
105511
+ var DISPOSABLE = /* @__PURE__ */ new Set([
105512
+ "node_modules",
105513
+ "dist",
105514
+ "build",
105515
+ "out",
105516
+ ".next",
105517
+ ".nuxt",
105518
+ ".svelte-kit",
105519
+ ".turbo",
105520
+ ".cache",
105521
+ ".parcel-cache",
105522
+ "coverage",
105523
+ ".nyc_output",
105524
+ "target",
105525
+ "__pycache__",
105526
+ ".pytest_cache",
105527
+ ".mypy_cache",
105528
+ ".ruff_cache",
105529
+ ".tox",
105530
+ "venv",
105531
+ ".venv",
105532
+ ".gradle",
105533
+ ".dart_tool",
105534
+ "tmp",
105535
+ ".tmp",
105536
+ ".vite",
105537
+ ".angular",
105538
+ ".expo",
105539
+ "DerivedData"
105540
+ ]);
105541
+ var SCRATCH_PREFIXES = ["/tmp/", "/private/tmp/", "/var/folders/", "/private/var/folders/"];
105542
+ function isDisposableTarget(raw) {
105543
+ let t2 = raw;
105544
+ if (t2 === "")
105545
+ return false;
105546
+ if (/[$~`]/.test(t2) || t2.includes(".."))
105547
+ return false;
105548
+ if (t2.startsWith("/"))
105549
+ return SCRATCH_PREFIXES.some((p) => t2.startsWith(p) && t2.length > p.length);
105550
+ t2 = t2.replace(/^(?:\.\/)+/, "").replace(/\/+$/, "");
105551
+ if (t2 === "" || t2 === ".")
105552
+ return false;
105553
+ const segs = t2.split("/").filter(Boolean);
105554
+ if (segs.length === 0)
105555
+ return false;
105556
+ return DISPOSABLE.has(segs[0]) || DISPOSABLE.has(segs[segs.length - 1]);
105557
+ }
105558
+ function checkGit(argsIn) {
105559
+ let i2 = 0;
105560
+ while (i2 < argsIn.length && argsIn[i2].startsWith("-")) {
105561
+ const a = argsIn[i2];
105562
+ i2 += ["-C", "-c", "--git-dir", "--work-tree", "--namespace", "--exec-path"].includes(a) ? 2 : 1;
105563
+ }
105564
+ const sub = argsIn[i2];
105565
+ const args = argsIn.slice(i2 + 1);
105566
+ const operands = args.filter((a) => !a.startsWith("-"));
105567
+ switch (sub) {
105568
+ case "push": {
105569
+ if (has(args, "--force", "f") || operands.some((a) => a.startsWith("+"))) {
105570
+ return hit("git-history", "force-pushes (can overwrite/destroy remote history)");
105571
+ }
105572
+ if (has(args, "--delete", "d") || operands.some((a) => a.startsWith(":"))) {
105573
+ return hit("git-history", "deletes a remote branch or tag");
105574
+ }
105575
+ if (has(args, "--mirror", null)) {
105576
+ return hit("git-history", "mirror-pushes (overwrites and deletes remote refs to match local)");
105577
+ }
105578
+ return null;
105579
+ }
105580
+ case "reset":
105581
+ if (has(args, "--hard", null) || has(args, "--keep", null) || has(args, "--merge", null)) {
105582
+ return hit("git-history", "hard-resets the working tree (discards uncommitted work)");
105583
+ }
105584
+ return null;
105585
+ case "clean":
105586
+ if (has(args, "--force", "f") && !has(args, "--dry-run", "n")) {
105587
+ return hit("git-history", "deletes untracked files (git clean)");
105588
+ }
105589
+ return null;
105590
+ case "branch":
105591
+ if (args.some((a) => /^-[A-Za-z]*D/.test(a)) || has(args, "--delete", "d") && has(args, "--force", "f")) {
105592
+ return hit("git-history", "force-deletes a git branch");
105593
+ }
105594
+ return null;
105595
+ case "checkout":
105596
+ if (has(args, "--force", "f"))
105597
+ return hit("git-history", "git checkout --force discards local changes");
105598
+ if (args.some((a) => BROAD.has(a)))
105599
+ return hit("git-history", "discards every uncommitted change in the working tree");
105600
+ return null;
105601
+ case "restore": {
105602
+ const stagedOnly = has(args, "--staged", "S") && !has(args, "--worktree", "W");
105603
+ if (!stagedOnly && args.some((a) => BROAD.has(a))) {
105604
+ return hit("git-history", "discards every uncommitted change in the working tree");
105605
+ }
105606
+ return null;
105607
+ }
105608
+ case "stash":
105609
+ if (args[0] === "drop" || args[0] === "clear")
105610
+ return hit("git-history", "drops stashed work (not recoverable without the commit hash)");
105611
+ return null;
105612
+ case "update-ref":
105613
+ if (has(args, "-d", "d") || has(args, "--delete", null))
105614
+ return hit("git-history", "deletes a git ref");
105615
+ return null;
105616
+ case "reflog":
105617
+ if (args[0] === "delete" || args[0] === "expire")
105618
+ return hit("git-history", "reflog expire / gc prune (permanently drops recoverable commits)");
105619
+ return null;
105620
+ case "gc":
105621
+ if (args.some((a) => /^--prune=(?:now|all)$/.test(a)))
105622
+ return hit("git-history", "reflog expire / gc prune (permanently drops recoverable commits)");
105623
+ return null;
105624
+ case "worktree":
105625
+ if (args[0] === "remove" && has(args, "--force", "f"))
105626
+ return hit("git-history", "force-removes a worktree (discards its uncommitted work)");
105627
+ return null;
105628
+ case "filter-branch":
105629
+ case "filter-repo":
105630
+ return hit("git-history", "rewrites git history (filter-branch/filter-repo)");
105631
+ case "rm":
105632
+ if (operands.some((a) => BROAD.has(a))) {
105633
+ return hit("git-history", "removes every tracked file from the working tree and index");
105634
+ }
105635
+ return null;
105636
+ default:
105637
+ return null;
105638
+ }
105639
+ }
105640
+ function checkRm(args) {
105641
+ const targets = [];
105642
+ let recursive = false;
105643
+ let endOpts = false;
105644
+ for (const a of args) {
105645
+ if (!endOpts && a === "--") {
105646
+ endOpts = true;
105647
+ continue;
105648
+ }
105649
+ if (!endOpts && a.startsWith("-")) {
105650
+ if (a === "--recursive" || /^-[A-Za-z]*[rR]/.test(a))
105651
+ recursive = true;
105652
+ continue;
105653
+ }
105654
+ targets.push(a);
105655
+ }
105656
+ if (targets.some((t2) => BROAD.has(t2)))
105657
+ return hit("filesystem", "deletes the current directory, home, or everything matching a bare glob");
105658
+ if (recursive && targets.some((t2) => !isDisposableTarget(t2))) {
105659
+ return hit("filesystem", "recursively deletes files or directories that are not a known build artefact");
105660
+ }
105661
+ return null;
105662
+ }
105663
+ function checkFind(args) {
105664
+ if (args.includes("-delete"))
105665
+ return hit("filesystem", "find -delete removes every match");
105666
+ for (let i2 = 0; i2 < args.length - 1; i2++) {
105667
+ if ((args[i2] === "-exec" || args[i2] === "-execdir" || args[i2] === "-ok") && ["rm", "shred", "unlink"].includes(base(args[i2 + 1]))) {
105668
+ return hit("filesystem", "find -exec rm removes every match");
105669
+ }
105670
+ }
105671
+ return null;
105672
+ }
105673
+ function judge(bin, args) {
105674
+ switch (bin) {
105675
+ case "git":
105676
+ return checkGit(args);
105677
+ case "rm":
105678
+ case "unlink":
105679
+ return checkRm(args);
105680
+ case "find":
105681
+ return checkFind(args);
105682
+ case "rsync":
105683
+ if (args.some((a) => a === "--del" || /^--delete(?:-[a-z]+)?$/.test(a)))
105684
+ return hit("filesystem", "rsync --delete removes destination files that are absent from the source");
105685
+ return null;
105686
+ case "truncate":
105687
+ return hit("filesystem", "truncate cuts a file (contents lost)");
105688
+ case "shred":
105689
+ return hit("filesystem", "shred irrecoverably overwrites a file");
105690
+ case "dd":
105691
+ if (args.some((a) => /^of=\/dev\//.test(a)))
105692
+ return hit("filesystem", "dd writing to a device (overwrites the disk)");
105693
+ return null;
105694
+ case "diskutil":
105695
+ if (/^(?:erase|secureErase|zeroDisk|randomDisk|partitionDisk|reformat)/i.test(args[0] ?? ""))
105696
+ return hit("filesystem", "diskutil erases or repartitions a disk");
105697
+ return null;
105698
+ case "chmod": {
105699
+ if (!has(args, "--recursive", "R"))
105700
+ return null;
105701
+ const operands = args.filter((a) => !a.startsWith("-"));
105702
+ if (operands.slice(1).some((t2) => BROAD.has(t2)))
105703
+ return hit("filesystem", "recursive chmod over the current directory / home / root");
105704
+ return null;
105705
+ }
105706
+ case "terraform":
105707
+ case "tofu":
105708
+ case "terragrunt":
105709
+ if (args.includes("destroy") || args.includes("apply") && (args.includes("-destroy") || args.includes("-auto-approve"))) {
105710
+ return hit("infra", "destroys or applies Terraform-managed infrastructure without a plan review");
105711
+ }
105712
+ return null;
105713
+ case "kubectl":
105714
+ case "oc": {
105715
+ const a = dropOpts(args, /* @__PURE__ */ new Set(["-n", "--namespace", "--context", "--cluster", "--kubeconfig", "-s", "--server", "--user"]));
105716
+ if (a[0] === "delete")
105717
+ return hit("infra", "deletes Kubernetes resources");
105718
+ if (a[0] === "drain")
105719
+ return hit("infra", "drains a node (evicts every pod)");
105720
+ if (a[0] === "replace" && has(a, "--force", null))
105721
+ return hit("infra", "kubectl replace --force deletes and recreates the resource");
105722
+ return null;
105723
+ }
105724
+ case "helm": {
105725
+ const a = dropOpts(args, /* @__PURE__ */ new Set(["-n", "--namespace", "--kube-context", "--kubeconfig"]));
105726
+ if (a[0] === "uninstall" || a[0] === "delete")
105727
+ return hit("infra", "helm uninstall / delete (removes a release)");
105728
+ return null;
105729
+ }
105730
+ case "aws":
105731
+ if (args[0] === "s3" && args[1] === "sync" && has(args, "--delete", null))
105732
+ return hit("cloud", "aws s3 sync --delete removes bucket objects missing locally");
105733
+ return null;
105734
+ case "vercel":
105735
+ if (args[0] === "remove" || args[0] === "rm")
105736
+ return hit("cloud", "removes a Vercel project or deployment");
105737
+ return null;
105738
+ case "fly":
105739
+ case "flyctl":
105740
+ if (args.includes("destroy"))
105741
+ return hit("cloud", "destroys a Fly.io app, volume or machine");
105742
+ return null;
105743
+ case "heroku":
105744
+ if (args.some((a) => /:destroy$/.test(a) || a === "pg:reset"))
105745
+ return hit("cloud", "destroys a Heroku app or resets its database");
105746
+ return null;
105747
+ case "supabase":
105748
+ if (args[0] === "db" && args[1] === "reset" || args[0] === "projects" && args[1] === "delete")
105749
+ return hit("cloud", "resets a Supabase database or deletes a project");
105750
+ return null;
105751
+ case "firebase":
105752
+ if (args.some((a) => /:delete$/.test(a)))
105753
+ return hit("cloud", "deletes Firebase data or a project");
105754
+ return null;
105755
+ case "netlify":
105756
+ if (args.some((a) => /:delete$/.test(a)))
105757
+ return hit("cloud", "deletes a Netlify site");
105758
+ return null;
105759
+ case "npm":
105760
+ case "pnpm":
105761
+ case "yarn":
105762
+ if (args[0] === "unpublish")
105763
+ return hit("publish", "unpublishes a package version");
105764
+ if (args[0] === "dist-tag" && (args[1] === "rm" || args[1] === "remove"))
105765
+ return hit("publish", "removes an npm dist-tag (can break `npm i pkg@latest`)");
105766
+ return null;
105767
+ case "gh": {
105768
+ if ((args[0] === "repo" || args[0] === "release") && args[1] === "delete")
105769
+ return hit("cloud", "deletes a GitHub repository or release");
105770
+ if (args[0] === "api") {
105771
+ const m2 = args.findIndex((a) => a === "-X" || a === "--method");
105772
+ const inline = args.find((a) => /^(?:-X|--method=)DELETE$/i.test(a));
105773
+ if (m2 >= 0 && /^delete$/i.test(args[m2 + 1] ?? "") || inline)
105774
+ return hit("cloud", "sends an HTTP DELETE to the GitHub API");
105775
+ }
105776
+ return null;
105777
+ }
105778
+ case "docker":
105779
+ case "docker-compose":
105780
+ case "podman": {
105781
+ const a = bin === "docker-compose" ? ["compose", ...args] : dropOpts(args, /* @__PURE__ */ new Set(["-H", "--host", "--context", "-c"]));
105782
+ const verb = a[0] === "container" ? a[1] : a[0];
105783
+ const rest = a.slice(a[0] === "container" ? 2 : 1);
105784
+ if (verb === "rm" && has(rest, "--force", "f") && rest.some((x2) => x2.startsWith("$")))
105785
+ return hit("container", "force-removes many containers at once");
105786
+ if (a[0] === "compose" && rest.includes("down") && has(rest, "--volumes", "v"))
105787
+ return hit("container", "docker compose down with -v (deletes named volumes)");
105788
+ return null;
105789
+ }
105790
+ case "dropdb":
105791
+ case "dropuser":
105792
+ return hit("database", "drops a Postgres database or role");
105793
+ default:
105794
+ if (bin.startsWith("mkfs"))
105795
+ return hit("filesystem", "mkfs formats a filesystem (destroys its contents)");
105796
+ return null;
105797
+ }
105798
+ }
105799
+ var DB_BINS = /* @__PURE__ */ new Set([
105800
+ "psql",
105801
+ "mysql",
105802
+ "mariadb",
105803
+ "mongo",
105804
+ "mongosh",
105805
+ "sqlite3",
105806
+ "cockroach",
105807
+ "clickhouse-client",
105808
+ "pgcli",
105809
+ "mycli",
105810
+ "usql",
105811
+ "sqlcmd",
105812
+ "redis-cli"
105813
+ ]);
105814
+ function classifyTokens(tokens, recurse, rawSql) {
105815
+ let t2 = tokens.slice();
105816
+ for (let guard = 0; guard < 12; guard++) {
105817
+ while (t2.length && (ENV_ASSIGN.test(t2[0]) || CONTROL_WORDS.has(t2[0])))
105818
+ t2 = t2.slice(1);
105819
+ if (t2.length === 0)
105820
+ return null;
105821
+ {
105822
+ const r2 = /^>(?![>&])/.test(t2[0]) ? t2 : (t2[0] === ":" || t2[0] === "true") && /^>(?![>&])/.test(t2[1] ?? "") ? t2.slice(1) : null;
105823
+ if (r2) {
105824
+ const target = r2[0].length > 1 ? r2[0].slice(1) : r2[1] ?? "";
105825
+ if (!target.startsWith("/dev/"))
105826
+ return hit("filesystem", "a bare `>` redirect truncates the file (contents lost)");
105827
+ return null;
105828
+ }
105829
+ }
105830
+ const bin = base(t2[0]);
105831
+ const args = t2.slice(1);
105832
+ if (DB_BINS.has(bin)) {
105833
+ return rawSql(bin + " " + args.map((a) => JSON.stringify(a)).join(" "));
105834
+ }
105835
+ if (bin === "sudo" || bin === "doas") {
105836
+ t2 = dropOpts(args, /* @__PURE__ */ new Set(["-u", "-g", "-h", "-p", "-C", "-D", "-R", "-T", "-U"]));
105837
+ continue;
105838
+ }
105839
+ if (bin === "env") {
105840
+ t2 = dropOpts(args, /* @__PURE__ */ new Set(["-u", "-C", "-S"]));
105841
+ continue;
105842
+ }
105843
+ if (bin === "nice") {
105844
+ t2 = dropOpts(args, /* @__PURE__ */ new Set(["-n"]));
105845
+ continue;
105846
+ }
105847
+ if (bin === "ionice") {
105848
+ t2 = dropOpts(args, /* @__PURE__ */ new Set(["-c", "-n", "-p"]));
105849
+ continue;
105850
+ }
105851
+ if (bin === "timeout") {
105852
+ t2 = dropOpts(args, /* @__PURE__ */ new Set(["-s", "-k"])).slice(1);
105853
+ continue;
105854
+ }
105855
+ if (bin === "xargs") {
105856
+ t2 = dropOpts(args, /* @__PURE__ */ new Set(["-I", "-n", "-P", "-L", "-s", "-d", "-E", "-a", "-J"]));
105857
+ continue;
105858
+ }
105859
+ if (["command", "builtin", "nohup", "exec", "time", "setsid", "caffeinate", "stdbuf", "unbuffer"].includes(bin)) {
105860
+ t2 = dropOpts(args);
105861
+ continue;
105862
+ }
105863
+ if (SHELLS.has(bin)) {
105864
+ const i2 = args.findIndex((a) => /^-[A-Za-z]*c[A-Za-z]*$/.test(a));
105865
+ if (i2 >= 0 && args[i2 + 1] !== void 0)
105866
+ return recurse(args[i2 + 1]);
105867
+ return null;
105868
+ }
105869
+ if (bin === "eval")
105870
+ return recurse(args.join(" "));
105871
+ if (bin === "ssh") {
105872
+ const a = dropOpts(args, /* @__PURE__ */ new Set(["-p", "-i", "-l", "-o", "-F", "-J", "-L", "-R", "-D", "-b", "-c", "-E", "-e", "-I", "-m", "-O", "-Q", "-S", "-w", "-W"]));
105873
+ return a.length > 1 ? recurse(a.slice(1).join(" ")) : null;
105874
+ }
105875
+ if (bin === "docker" || bin === "podman" || bin === "docker-compose") {
105876
+ const a0 = bin === "docker-compose" ? ["compose", ...args] : dropOpts(args, /* @__PURE__ */ new Set(["-H", "--host", "--context", "-c"]));
105877
+ const isExec = a0[0] === "exec" || a0[0] === "compose" && dropOpts(a0.slice(1), /* @__PURE__ */ new Set(["-f", "-p", "--project-name", "--file", "--profile"]))[0] === "exec";
105878
+ if (isExec) {
105879
+ const afterVerb = a0[0] === "exec" ? a0.slice(1) : dropOpts(a0.slice(1), /* @__PURE__ */ new Set(["-f", "-p", "--project-name", "--file", "--profile"])).slice(1);
105880
+ const inner = dropOpts(afterVerb, /* @__PURE__ */ new Set(["-e", "-u", "-w", "--env", "--user", "--workdir", "--env-file", "--detach-keys", "--index"]));
105881
+ if (inner.length > 1) {
105882
+ t2 = inner.slice(1);
105883
+ continue;
105884
+ }
105885
+ return null;
105886
+ }
105887
+ }
105888
+ if (bin === "kubectl" || bin === "oc") {
105889
+ const i2 = args.indexOf("--");
105890
+ if (i2 >= 0 && args.includes("exec") && i2 + 1 < args.length) {
105891
+ t2 = args.slice(i2 + 1);
105892
+ continue;
105893
+ }
105894
+ }
105895
+ return judge(bin, args);
105896
+ }
105897
+ return null;
105898
+ }
105899
+ }
105900
+ });
105901
+
105418
105902
  // ../../../../../Users/henry/Documents/nexrall-code/packages/core/dist/permissions/destructive.js
105419
105903
  var require_destructive = __commonJS({
105420
105904
  "../../../../../Users/henry/Documents/nexrall-code/packages/core/dist/permissions/destructive.js"(exports2) {
@@ -105422,6 +105906,7 @@ var require_destructive = __commonJS({
105422
105906
  Object.defineProperty(exports2, "__esModule", { value: true });
105423
105907
  exports2.classifyDestructiveCommand = classifyDestructiveCommand;
105424
105908
  exports2.isDestructiveBash = isDestructiveBash3;
105909
+ var destructiveTokens_1 = require_destructiveTokens();
105425
105910
  function stripHeredocs(command) {
105426
105911
  return command.replace(/<<-?\s*(['"]?)(\w+)\1[\s\S]*?\n\2(?=\s|;|&|$)/g, " ");
105427
105912
  }
@@ -105451,6 +105936,16 @@ var require_destructive = __commonJS({
105451
105936
  reason: "drops a Postgres/MySQL role or user",
105452
105937
  re: new RegExp(CMD_POS + DB_CLIENT + String.raw`\b[\s\S]*?\bdrop\s+(?:role|user)\b`, "i")
105453
105938
  },
105939
+ {
105940
+ category: "database",
105941
+ reason: "UPDATE without a WHERE clause (rewrites every row)",
105942
+ re: new RegExp(CMD_POS + DB_CLIENT + String.raw`\b[\s\S]*?\bupdate\s+[\w.` + "`" + String.raw`"]+\s+set\b(?![\s\S]*\bwhere\b)`, "i")
105943
+ },
105944
+ {
105945
+ category: "database",
105946
+ reason: "DELETE with a WHERE clause that is always true (deletes every row)",
105947
+ re: new RegExp(CMD_POS + DB_CLIENT + String.raw`\b[\s\S]*?\bdelete\s+from\b[\s\S]*?\bwhere\s+(?:1\s*=\s*1|true)\b`, "i")
105948
+ },
105454
105949
  {
105455
105950
  category: "database",
105456
105951
  reason: "drops a MongoDB database or collection",
@@ -105490,11 +105985,6 @@ var require_destructive = __commonJS({
105490
105985
  reason: "deletes untracked files (git clean)",
105491
105986
  re: /\bgit\s+clean\s+-[a-z]*f/i
105492
105987
  },
105493
- {
105494
- category: "git-history",
105495
- reason: "force-deletes a git branch",
105496
- re: /\bgit\s+branch\s+-[a-z]*D/i
105497
- },
105498
105988
  {
105499
105989
  category: "git-history",
105500
105990
  reason: "rewrites git history (filter-branch/filter-repo)",
@@ -105585,9 +106075,7 @@ var require_destructive = __commonJS({
105585
106075
  re: new RegExp(CMD_POS + String.raw`(?:npm\s+publish|yarn\s+publish|pnpm\s+publish|cargo\s+publish|twine\s+upload|gem\s+push)\b`, "i")
105586
106076
  }
105587
106077
  ];
105588
- function classifyDestructiveCommand(command) {
105589
- if (!command || typeof command !== "string")
105590
- return null;
106078
+ function classifyRegex(command) {
105591
106079
  const raw = stripHeredocs(command);
105592
106080
  for (const rule of RAW_RULES) {
105593
106081
  if (rule.re.test(raw)) {
@@ -105602,6 +106090,33 @@ var require_destructive = __commonJS({
105602
106090
  }
105603
106091
  return null;
105604
106092
  }
106093
+ function classifyRawSql(command) {
106094
+ const raw = stripHeredocs(command);
106095
+ for (const rule of RAW_RULES) {
106096
+ if (rule.re.test(raw))
106097
+ return { category: rule.category, reason: rule.reason };
106098
+ }
106099
+ return null;
106100
+ }
106101
+ var MAX_DEPTH = 4;
106102
+ function classify(command, depth) {
106103
+ if (depth > MAX_DEPTH)
106104
+ return null;
106105
+ const viaRegex = classifyRegex(command);
106106
+ if (viaRegex)
106107
+ return viaRegex;
106108
+ for (const tokens of (0, destructiveTokens_1.tokenize)(stripHeredocs(command))) {
106109
+ const m2 = (0, destructiveTokens_1.classifyTokens)(tokens, (inner) => classify(inner, depth + 1), classifyRawSql);
106110
+ if (m2)
106111
+ return m2;
106112
+ }
106113
+ return null;
106114
+ }
106115
+ function classifyDestructiveCommand(command) {
106116
+ if (!command || typeof command !== "string")
106117
+ return null;
106118
+ return classify(command, 0);
106119
+ }
105605
106120
  function isDestructiveBash3(tool, input) {
105606
106121
  if (tool !== "bash" && tool !== "run_command" && tool !== "execute_command")
105607
106122
  return null;
@@ -145676,6 +146191,67 @@ function padVis(s2, n) {
145676
146191
  const pad = n - visibleLen(s2);
145677
146192
  return pad > 0 ? s2 + " ".repeat(pad) : s2;
145678
146193
  }
146194
+ var graphemeSegmenter = typeof Intl.Segmenter === "function" ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null;
146195
+ function graphemes(s2) {
146196
+ return graphemeSegmenter ? Array.from(graphemeSegmenter.segment(s2), (x2) => x2.segment) : Array.from(s2);
146197
+ }
146198
+ function truncateCells(s2, max) {
146199
+ if (max <= 0)
146200
+ return "";
146201
+ if (visibleLen(s2) <= max)
146202
+ return s2;
146203
+ let out = "";
146204
+ for (const g of graphemes(s2)) {
146205
+ if (visibleLen(out + g) > max - 1)
146206
+ break;
146207
+ out += g;
146208
+ }
146209
+ return out + "\u2026";
146210
+ }
146211
+ function wrapCells(text, width) {
146212
+ if (width < 1)
146213
+ return [];
146214
+ const rows = [];
146215
+ let cur = "";
146216
+ for (const w of text.split(/\s+/)) {
146217
+ if (!w)
146218
+ continue;
146219
+ let word = w;
146220
+ while (visibleLen(word) > width) {
146221
+ if (cur) {
146222
+ rows.push(cur);
146223
+ cur = "";
146224
+ }
146225
+ const g = graphemes(word);
146226
+ let chunk = "";
146227
+ let i2 = 0;
146228
+ for (; i2 < g.length; i2++) {
146229
+ if (visibleLen(chunk + g[i2]) > width)
146230
+ break;
146231
+ chunk += g[i2];
146232
+ }
146233
+ if (!chunk) {
146234
+ chunk = g[0];
146235
+ i2 = 1;
146236
+ }
146237
+ rows.push(chunk);
146238
+ word = g.slice(i2).join("");
146239
+ }
146240
+ if (!word)
146241
+ continue;
146242
+ if (!cur)
146243
+ cur = word;
146244
+ else if (visibleLen(cur) + 1 + visibleLen(word) <= width)
146245
+ cur += " " + word;
146246
+ else {
146247
+ rows.push(cur);
146248
+ cur = word;
146249
+ }
146250
+ }
146251
+ if (cur)
146252
+ rows.push(cur);
146253
+ return rows;
146254
+ }
145679
146255
  function ruleWidth(max = 50, columns = process.stdout.columns || 80) {
145680
146256
  const MIN = 20;
145681
146257
  const indent = 2;
@@ -145904,11 +146480,9 @@ function formatToolResult(name, result, durationMs) {
145904
146480
  const sizeHint = lines > 1 ? `${lines} lines` : `${out.length} chars`;
145905
146481
  return source_default.green(icons.check) + " " + source_default.dim(name) + source_default.dim(` \xB7 ${sizeHint}`) + source_default.dim(` \xB7 ${durationMs}ms`);
145906
146482
  }
145907
- function formatThinking(text, tokens) {
146483
+ function formatThinking(tokens) {
145908
146484
  const tokStr = tokens >= 1e3 ? `~${(tokens / 1e3).toFixed(1)}k tokens` : tokens > 0 ? `~${tokens} tokens` : "";
145909
- const header = source_default.dim(`${icons.thinking} Thought`) + (tokStr ? source_default.dim(` for ${tokStr}`) : "");
145910
- const preview = text.trim().split("\n").filter(Boolean).slice(0, 3).map((l2) => source_default.dim(" " + l2.slice(0, 100))).join("\n");
145911
- return "\n" + header + (preview ? "\n" + preview : "") + "\n";
146485
+ return source_default.dim(`${icons.thinking} Thought`) + (tokStr ? source_default.dim(` \xB7 ${tokStr}`) : "");
145912
146486
  }
145913
146487
  function formatUsage(outputTokens) {
145914
146488
  const tok = outputTokens >= 1e3 ? `${(outputTokens / 1e3).toFixed(1)}k` : String(outputTokens);
@@ -157661,11 +158235,11 @@ function fillerContent(rows) {
157661
158235
  return "\n".repeat(rows - 1);
157662
158236
  }
157663
158237
  var IntlWithSegmenter2 = Intl;
157664
- var graphemeSegmenter = typeof IntlWithSegmenter2.Segmenter === "function" ? new IntlWithSegmenter2.Segmenter(void 0, { granularity: "grapheme" }) : null;
158238
+ var graphemeSegmenter2 = typeof IntlWithSegmenter2.Segmenter === "function" ? new IntlWithSegmenter2.Segmenter(void 0, { granularity: "grapheme" }) : null;
157665
158239
  function graphemeBoundaries(text) {
157666
- if (graphemeSegmenter) {
158240
+ if (graphemeSegmenter2) {
157667
158241
  const bounds2 = [0];
157668
- for (const { index } of graphemeSegmenter.segment(text)) {
158242
+ for (const { index } of graphemeSegmenter2.segment(text)) {
157669
158243
  if (index > 0)
157670
158244
  bounds2.push(index);
157671
158245
  }
@@ -158965,7 +159539,95 @@ ${content}`);
158965
159539
  }
158966
159540
  return parts.length > 0 ? parts.join("\n\n---\n\n") : void 0;
158967
159541
  }
158968
- var THINK_TAIL_CHARS = 48;
159542
+ var THINK_PREVIEW_ROWS = 3;
159543
+ var THINK_BUFFER_CHARS = 1200;
159544
+ var VERB_ROTATE_SEC = 3;
159545
+ var WORKING_VERBS = [
159546
+ "Accomplishing",
159547
+ "Actualizing",
159548
+ "Baking",
159549
+ "Brewing",
159550
+ "Calculating",
159551
+ "Cerebrating",
159552
+ "Churning",
159553
+ "Coalescing",
159554
+ "Cogitating",
159555
+ "Computing",
159556
+ "Concocting",
159557
+ "Conjuring",
159558
+ "Considering",
159559
+ "Contemplating",
159560
+ "Cooking",
159561
+ "Crafting",
159562
+ "Creating",
159563
+ "Crunching",
159564
+ "Deliberating",
159565
+ "Determining",
159566
+ "Digesting",
159567
+ "Distilling",
159568
+ "Divining",
159569
+ "Effecting",
159570
+ "Elaborating",
159571
+ "Envisioning",
159572
+ "Fabricating",
159573
+ "Fermenting",
159574
+ "Finagling",
159575
+ "Forging",
159576
+ "Formulating",
159577
+ "Generating",
159578
+ "Germinating",
159579
+ "Hatching",
159580
+ "Herding",
159581
+ "Hustling",
159582
+ "Ideating",
159583
+ "Imagining",
159584
+ "Incubating",
159585
+ "Inferring",
159586
+ "Manifesting",
159587
+ "Marinating",
159588
+ "Meandering",
159589
+ "Mulling",
159590
+ "Musing",
159591
+ "Noodling",
159592
+ "Percolating",
159593
+ "Pondering",
159594
+ "Processing",
159595
+ "Puzzling",
159596
+ "Reticulating",
159597
+ "Ruminating",
159598
+ "Simmering",
159599
+ "Smelting",
159600
+ "Spinning",
159601
+ "Stewing",
159602
+ "Sussing",
159603
+ "Synthesizing",
159604
+ "Thinking",
159605
+ "Tinkering",
159606
+ "Transmuting",
159607
+ "Unfurling",
159608
+ "Vibing",
159609
+ "Wandering",
159610
+ "Weaving",
159611
+ "Whirring",
159612
+ "Working"
159613
+ ];
159614
+ function composeThinkingBlock(opts) {
159615
+ const tok = formatProgressTokens(opts.tokens);
159616
+ const flat = opts.text.replace(/\s+/g, " ").trim();
159617
+ if (!tok && !flat)
159618
+ return [];
159619
+ const columns = Math.max(20, opts.columns | 0 || 80);
159620
+ const termRows = opts.rows && opts.rows > 0 ? opts.rows : 24;
159621
+ const maxRows = Math.max(0, Math.min(opts.maxRows ?? THINK_PREVIEW_ROWS, termRows - 12));
159622
+ const lines = [source_default.dim(truncateCells(`${icons.thinking} Thinking\u2026${tok ? " " + tok : ""}`, columns - 1))];
159623
+ if (flat && maxRows > 0) {
159624
+ const indent = " ";
159625
+ const wrapped = wrapCells(flat, columns - 1 - indent.length);
159626
+ for (const row of wrapped.slice(-maxRows))
159627
+ lines.push(source_default.dim.italic(indent + row));
159628
+ }
159629
+ return lines;
159630
+ }
158969
159631
  function composeStatusLine(opts) {
158970
159632
  const timer = source_default.dim(` (${opts.elapsedSec}s)`);
158971
159633
  const hint = opts.hint !== false && !opts.detail && opts.elapsedSec >= 15 ? source_default.dim(" \xB7 still running, no output yet is normal for quiet commands") : "";
@@ -158994,10 +159656,21 @@ var Spinner = class {
158994
159656
  // already explained by the detail text (e.g. a live token counter is itself
158995
159657
  // proof of life, so "no output yet is normal" would be noise).
158996
159658
  hintEnabled = true;
159659
+ // Model-working phases (before the first token, and between tool rounds) show a rotating
159660
+ // verb instead of a fixed label, plus the live reasoning block above it. Tool phases
159661
+ // ("running bash…") keep their literal label: a verb there would hide what is running.
159662
+ verbs = false;
159663
+ verbIdx = 0;
159664
+ thinkTokens = 0;
159665
+ thinkText = "";
158997
159666
  start(text, opts) {
158998
159667
  this.stop();
158999
159668
  this.baseText = text;
159000
159669
  this.detail = "";
159670
+ this.thinkTokens = 0;
159671
+ this.thinkText = "";
159672
+ this.verbs = opts?.verbs === true;
159673
+ this.verbIdx = Math.floor(Math.random() * WORKING_VERBS.length);
159001
159674
  this.hintEnabled = opts?.hint !== false;
159002
159675
  this.startedAt = Date.now();
159003
159676
  this.render();
@@ -159027,6 +159700,17 @@ var Spinner = class {
159027
159700
  return;
159028
159701
  this.detail = detail;
159029
159702
  }
159703
+ /**
159704
+ * Feed the live reasoning block (token count + streamed text). Like setDetail, this only
159705
+ * records state — the 100 ms tick renders it, so a burst of deltas cannot add redraws.
159706
+ * The text is capped to THINK_BUFFER_CHARS: only its last rows are ever displayed.
159707
+ */
159708
+ setThinking(tokens, text) {
159709
+ if (this.interval === null)
159710
+ return;
159711
+ this.thinkTokens = tokens;
159712
+ this.thinkText = text.slice(-THINK_BUFFER_CHARS);
159713
+ }
159030
159714
  /** Swap the label without resetting the elapsed timer (phase change within one turn). Same reasoning as setDetail() above: no immediate render, the interval tick picks it up. */
159031
159715
  setText(text) {
159032
159716
  if (this.interval === null)
@@ -159034,20 +159718,36 @@ var Spinner = class {
159034
159718
  this.baseText = text;
159035
159719
  }
159036
159720
  render() {
159037
- getInkTerminal()?.setLive(composeStatusLine({
159721
+ const elapsedSec = Math.floor((Date.now() - this.startedAt) / 1e3);
159722
+ if (this.verbs && elapsedSec > 0 && elapsedSec % VERB_ROTATE_SEC === 0 && this.lastRotateSec !== elapsedSec) {
159723
+ this.lastRotateSec = elapsedSec;
159724
+ this.verbIdx++;
159725
+ }
159726
+ const label = this.verbs ? `${WORKING_VERBS[this.verbIdx % WORKING_VERBS.length]}\u2026` : this.baseText;
159727
+ const status = composeStatusLine({
159038
159728
  frame: this.frames[this.i % this.frames.length],
159039
- text: this.baseText,
159040
- elapsedSec: Math.floor((Date.now() - this.startedAt) / 1e3),
159729
+ text: label,
159730
+ elapsedSec,
159041
159731
  detail: this.detail,
159042
159732
  hint: this.hintEnabled
159043
- }));
159733
+ });
159734
+ const block = this.verbs ? composeThinkingBlock({
159735
+ tokens: this.thinkTokens,
159736
+ text: this.thinkText,
159737
+ columns: process.stdout.columns || 80,
159738
+ rows: process.stdout.rows || 24
159739
+ }) : [];
159740
+ getInkTerminal()?.setLive(block.length ? block.join("\n") + "\n" + status : status);
159044
159741
  this.i++;
159045
159742
  }
159743
+ lastRotateSec = -1;
159046
159744
  stop() {
159047
159745
  if (this.interval !== null) {
159048
159746
  clearInterval(this.interval);
159049
159747
  this.interval = null;
159050
159748
  this.detail = "";
159749
+ this.thinkTokens = 0;
159750
+ this.thinkText = "";
159051
159751
  getInkTerminal()?.setLive("");
159052
159752
  }
159053
159753
  }
@@ -159142,26 +159842,20 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
159142
159842
  const toolCalls = /* @__PURE__ */ new Map();
159143
159843
  const subPrefix = (meta) => source_default.dim(` \u21B3 ${meta?.agentName ?? "sub-agent"} `);
159144
159844
  let thinkingTokens = 0;
159145
- let liveThinkTail = "";
159845
+ let liveThinkText = "";
159146
159846
  const pushStatus = () => {
159147
- const parts = [];
159148
- const tok = formatProgressTokens(thinkingTokens);
159149
- if (tok)
159150
- parts.push(tok);
159151
- if (liveThinkTail)
159152
- parts.push(liveThinkTail);
159153
- spinner.setDetail(parts.join(" \xB7 "));
159847
+ spinner.setThinking(thinkingTokens, liveThinkText);
159154
159848
  };
159155
159849
  const resumeWorking = (label = "working\u2026") => {
159156
159850
  if (abortSignal.aborted)
159157
159851
  return;
159158
159852
  thinkingTokens = 0;
159159
- liveThinkTail = "";
159160
- spinner.start(label, { hint: false });
159853
+ liveThinkText = "";
159854
+ spinner.start(label, { hint: false, verbs: true });
159161
159855
  };
159162
159856
  setMode(mode);
159163
159857
  console.log();
159164
- spinner.start("thinking\u2026", { hint: false });
159858
+ spinner.start("thinking\u2026", { hint: false, verbs: true });
159165
159859
  const result = await (0, import_code_core7.runAgentLoop)(messages, {
159166
159860
  workDir,
159167
159861
  model: modelAlias,
@@ -159213,17 +159907,16 @@ async function runTurn(messages, modelAlias, workDir, abortSignal, env4, nexrall
159213
159907
  onThinkingDelta: (text) => {
159214
159908
  if (abortSignal.aborted)
159215
159909
  return;
159216
- const flat = text.replace(/\s+/g, " ");
159217
- liveThinkTail = (liveThinkTail + flat).slice(-THINK_TAIL_CHARS);
159910
+ liveThinkText = (liveThinkText + text).slice(-THINK_BUFFER_CHARS);
159218
159911
  pushStatus();
159219
159912
  },
159220
159913
  onThinking: (text) => {
159221
159914
  if (abortSignal.aborted)
159222
159915
  return;
159223
159916
  spinner.stop();
159224
- console.log(formatThinking(text, thinkingTokens));
159917
+ console.log(formatThinking(thinkingTokens));
159225
159918
  thinkingTokens = 0;
159226
- liveThinkTail = "";
159919
+ liveThinkText = "";
159227
159920
  resumeWorking();
159228
159921
  },
159229
159922
  onText: (text) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexrall-code",
3
- "version": "0.5.126",
3
+ "version": "0.5.127",
4
4
  "description": "Nexrall Code — AI coding assistant for your terminal (headless agent for scripts, CI and automation)",
5
5
  "keywords": [
6
6
  "ai",