@gotgenes/pi-permission-system 33.1.0 → 33.1.1

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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [33.1.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v33.1.0...pi-permission-system-v33.1.1) (2026-09-24)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **pi-permission-system:** read a recognized flag's quoted value as its value ([a9721e4](https://github.com/gotgenes/pi-packages/commit/a9721e43828ee499d6a78fa2f03d4d6c87a15a95)), closes [#957](https://github.com/gotgenes/pi-packages/issues/957)
14
+ * **pi-permission-system:** admit a quoted flag by its unquoted leading dash ([#957](https://github.com/gotgenes/pi-packages/issues/957)) ([e5eb6ab](https://github.com/gotgenes/pi-packages/commit/e5eb6ab55ec2aae5ad0b8254f46ad90b59ca56aa)), closes [#972](https://github.com/gotgenes/pi-packages/issues/972)
15
+
8
16
  ## [33.1.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v33.0.8...pi-permission-system-v33.1.0) (2026-09-23)
9
17
 
10
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-permission-system",
3
- "version": "33.1.0",
3
+ "version": "33.1.1",
4
4
  "description": "Permission enforcement extension for the Pi coding agent.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -779,33 +779,40 @@ function collectPatternCommandTokens(
779
779
  }
780
780
 
781
781
  // Flag detection (only before "--" end-of-flags marker).
782
- if (
783
- !pastEndOfFlags &&
784
- child.type === "word" &&
785
- text.startsWith("-") &&
786
- text.length > 1
787
- ) {
782
+ //
783
+ // A quoted flag value (`grep --regexp='/etc/passwd'`, `awk -F':'`) makes
784
+ // the argument a `concatenation`, so a recognized flag is acted on in that
785
+ // shape too. An unrecognized one is not, and neither is a token quoted
786
+ // whole (`sd '-old' '-new' file.txt`): reading either as a flag would stop
787
+ // a quoted `-`-leading *pattern* from spending its positional and drop the
788
+ // real operand, where leaving it positional only over-surfaces (#957).
789
+ if (!pastEndOfFlags && text.startsWith("-") && text.length > 1) {
788
790
  const directive = classifyPatternCommandFlag(text, config);
789
- switch (directive.kind) {
790
- case "end-of-flags":
791
- pastEndOfFlags = true;
792
- break;
793
- case "consume-next":
794
- pendingConsumption = directive.role;
795
- if (suppliesScript(directive.role)) hasExplicitScript = true;
796
- break;
797
- case "inline-value":
798
- if (directive.role === "script-file")
799
- tokens.push({ token: directive.value, effect });
800
- if (suppliesScript(directive.role)) hasExplicitScript = true;
801
- break;
802
- case "regular-flag":
803
- // Unrecognized: fall back to the blind `--opt=value` split, which is
804
- // safe precisely because the flag's role is unknown (#645).
805
- tokens.push(...embeddedOptionValueToken(text, effect));
806
- break;
791
+ if (
792
+ child.type === "word" ||
793
+ (directive.kind !== "regular-flag" && hasUnquotedLeadingDash(child))
794
+ ) {
795
+ switch (directive.kind) {
796
+ case "end-of-flags":
797
+ pastEndOfFlags = true;
798
+ break;
799
+ case "consume-next":
800
+ pendingConsumption = directive.role;
801
+ if (suppliesScript(directive.role)) hasExplicitScript = true;
802
+ break;
803
+ case "inline-value":
804
+ if (directive.role === "script-file")
805
+ tokens.push({ token: directive.value, effect });
806
+ if (suppliesScript(directive.role)) hasExplicitScript = true;
807
+ break;
808
+ case "regular-flag":
809
+ // Unrecognized: fall back to the blind `--opt=value` split, which is
810
+ // safe precisely because the flag's role is unknown (#645).
811
+ tokens.push(...embeddedOptionValueToken(text, effect));
812
+ break;
813
+ }
814
+ continue;
807
815
  }
808
- continue;
809
816
  }
810
817
 
811
818
  // Positional argument.
@@ -814,14 +821,25 @@ function collectPatternCommandTokens(
814
821
  } else {
815
822
  tokens.push({ token: text, effect });
816
823
  }
817
- // A quoted flag never reaches the flag branch above, so its embedded value
818
- // is split here instead.
824
+ // A quoted token that did not act as a flag above has its embedded value
825
+ // split here instead.
819
826
  tokens.push(...embeddedOptionValueToken(text, effect));
820
827
  }
821
828
 
822
829
  return tokens;
823
830
  }
824
831
 
832
+ /**
833
+ * Whether a `concatenation` argument begins with an unquoted `-`, as a flag
834
+ * carrying a quoted value does (`-F':'` is the word `-F` then `':'`). A token
835
+ * quoted whole is a `raw_string` or `string`, never a `concatenation`.
836
+ */
837
+ function hasUnquotedLeadingDash(child: TSNode): boolean {
838
+ if (child.type !== "concatenation") return false;
839
+ const head = child.child(0);
840
+ return head?.type === "word" && head.text.startsWith("-");
841
+ }
842
+
825
843
  /**
826
844
  * Whether a flag in this role means the inline pattern positional is spent.
827
845
  *
@@ -44,7 +44,7 @@ export function composeAuthorizerChain(
44
44
  if (decision) {
45
45
  return decision;
46
46
  }
47
- // `defer` \u2014 try the next link.
47
+ // `defer` — try the next link.
48
48
  }
49
49
  return terminal.authorize(details);
50
50
  },