Describe the bug
In plan mode, the command gate that is supposed to permit read-only investigation and block workspace mutations produces false positives for several read-only invocations of the gh CLI. Specifically, gh api <path> (an HTTP GET) and gh api graphql -f query='query{...}' (a read-only GraphQL query) are both blocked with "This shell command may modify the workspace and was blocked. Plan mode does not permit changes to the workspace." Separately, an otherwise-allowed read command (gh pr view ... --json ...) becomes blocked the moment it is piped into python (e.g. | python -c ...), even though the pipeline has no side effects.
The classifier appears to key on the surface tokens (gh api, and "pipes into an interpreter") rather than on the actual HTTP verb / mutation potential. This is a CLI-side command-classification issue, not a shell, OS, or antivirus problem: the blocked commands are pure reads with no filesystem or repo side effects.
Impact: plan mode's whole purpose is safe read-only investigation before proposing a plan. Inline pull-request review-thread comments are only retrievable via gh api / gh api graphql; blocking those forces the user either to abandon plan mode or to fall back to dumping raw JSON to a temp file and re-reading it with a file viewer, which still cannot reach inline review comments at all.
Affected version
GitHub Copilot CLI 1.0.72-1
Steps to reproduce the behavior
- Enter plan mode (Shift+Tab).
- Run
gh pr view <prId> --repo <repo> --json state,mergeStateStatus -> it runs and returns JSON (correctly allowed as read-only).
- Run
gh api repos/<repo>/pulls/<prId>/comments -> blocked with "This shell command may modify the workspace and was blocked. Plan mode does not permit changes to the workspace." This is a plain HTTP GET.
- Run
gh api graphql -f query='query($o:String!){repository(owner:$o,name:"x"){id}}' -F o=bic -> blocked with the same message. This is a read-only GraphQL query (no mutation).
- Run
gh pr view <prId> --repo <repo> --json comments | python -c "import sys;print(len(sys.stdin.read()))" -> blocked, even though step 2 shows gh pr view alone is allowed; the pipe into python is what flips it to blocked.
Example with internal repo/pr can be specified if needed.
Expected behavior
In plan mode the command gate must permit read-only commands. A bare gh api <path> GET and a gh api graphql request whose document is a query (not a mutation) do not modify the workspace and should be allowed. Piping an allowed read command into an interpreter (python, node, jq) purely to format or count output should not, by itself, cause a block; a pipeline should be judged by the mutation potential of its stages, not blanket-blocked because one stage is an interpreter reading stdin.
Additional context
- Logs: can attach
copilot debug-logs --session ffe57d43-4a60-40c9-b1a9-99614e7b5a2d on request.
- Operating system: Windows (Windows_NT), PowerShell 5.x.
- CPU architecture: x86_64.
- Terminal emulator: Windows Terminal preview.
- Shell: PowerShell (powershell.exe).
- Ownership: this is the CLI's plan-mode command classifier, not the terminal/OS. The blocked commands are read-only HTTP requests with no side effects; the same session shows a sibling read command (
gh pr view --json) is allowed, so the gate clearly has the notion of read-only but is mis-bucketing gh api and interpreter pipes.
- Proposed fix (approx): in the plan-mode command classifier, do not treat
gh api as mutating unless it carries a write verb: --method POST|PATCH|PUT|DELETE / -X <verb>, field-write flags (-f/--raw-field/-F/--field) on a non-graphql call, or a graphql document whose top-level operation is mutation. Treat bare gh api <path> and gh api graphql -f query='query ...' as read. For pipelines, classify by the union of stage effects rather than blocking any pipe whose downstream stage is python/node/jq reading stdin.
- Regression guard: add plan-mode classifier tests asserting
gh api <path> (GET) and gh api graphql with a query document are ALLOWED, while gh api -X POST ... and gh api graphql with a mutation document are BLOCKED; and that <allowed-read> | python -c ... remains allowed.
- Workaround used this session:
gh pr view 56494 --json comments,reviews,statusCheckRollup (allowed) dumped to stdout/temp file and read with the file viewer; but this exposes only the PR-level review body, NOT the inline review-thread comment, which requires the blocked gh api/gh api graphql.
Describe the bug
In plan mode, the command gate that is supposed to permit read-only investigation and block workspace mutations produces false positives for several read-only invocations of the
ghCLI. Specifically,gh api <path>(an HTTP GET) andgh api graphql -f query='query{...}'(a read-only GraphQL query) are both blocked with "This shell command may modify the workspace and was blocked. Plan mode does not permit changes to the workspace." Separately, an otherwise-allowed read command (gh pr view ... --json ...) becomes blocked the moment it is piped intopython(e.g.| python -c ...), even though the pipeline has no side effects.The classifier appears to key on the surface tokens (
gh api, and "pipes into an interpreter") rather than on the actual HTTP verb / mutation potential. This is a CLI-side command-classification issue, not a shell, OS, or antivirus problem: the blocked commands are pure reads with no filesystem or repo side effects.Impact: plan mode's whole purpose is safe read-only investigation before proposing a plan. Inline pull-request review-thread comments are only retrievable via
gh api/gh api graphql; blocking those forces the user either to abandon plan mode or to fall back to dumping raw JSON to a temp file and re-reading it with a file viewer, which still cannot reach inline review comments at all.Affected version
GitHub Copilot CLI 1.0.72-1
Steps to reproduce the behavior
gh pr view <prId> --repo <repo> --json state,mergeStateStatus-> it runs and returns JSON (correctly allowed as read-only).gh api repos/<repo>/pulls/<prId>/comments-> blocked with "This shell command may modify the workspace and was blocked. Plan mode does not permit changes to the workspace." This is a plain HTTP GET.gh api graphql -f query='query($o:String!){repository(owner:$o,name:"x"){id}}' -F o=bic-> blocked with the same message. This is a read-only GraphQL query (nomutation).gh pr view <prId> --repo <repo> --json comments | python -c "import sys;print(len(sys.stdin.read()))"-> blocked, even though step 2 showsgh pr viewalone is allowed; the pipe intopythonis what flips it to blocked.Example with internal repo/pr can be specified if needed.
Expected behavior
In plan mode the command gate must permit read-only commands. A bare
gh api <path>GET and agh api graphqlrequest whose document is aquery(not amutation) do not modify the workspace and should be allowed. Piping an allowed read command into an interpreter (python, node, jq) purely to format or count output should not, by itself, cause a block; a pipeline should be judged by the mutation potential of its stages, not blanket-blocked because one stage is an interpreter reading stdin.Additional context
copilot debug-logs --session ffe57d43-4a60-40c9-b1a9-99614e7b5a2don request.gh pr view --json) is allowed, so the gate clearly has the notion of read-only but is mis-bucketinggh apiand interpreter pipes.gh apias mutating unless it carries a write verb:--method POST|PATCH|PUT|DELETE/-X <verb>, field-write flags (-f/--raw-field/-F/--field) on a non-graphql call, or a graphql document whose top-level operation ismutation. Treat baregh api <path>andgh api graphql -f query='query ...'as read. For pipelines, classify by the union of stage effects rather than blocking any pipe whose downstream stage ispython/node/jqreading stdin.gh api <path>(GET) andgh api graphqlwith aquerydocument are ALLOWED, whilegh api -X POST ...andgh api graphqlwith amutationdocument are BLOCKED; and that<allowed-read> | python -c ...remains allowed.gh pr view 56494 --json comments,reviews,statusCheckRollup(allowed) dumped to stdout/temp file and read with the file viewer; but this exposes only the PR-level review body, NOT the inline review-thread comment, which requires the blockedgh api/gh api graphql.