dotagents-cli 0.9.0__tar.gz → 0.9.1__tar.gz

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 (139) hide show
  1. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/CHANGELOG.md +13 -1
  2. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/PKG-INFO +1 -1
  3. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/guide/commands.md +6 -4
  4. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/__init__.py +1 -1
  5. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_agents.py +8 -7
  6. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/init.py +3 -2
  7. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_powershell_env_hook.py +29 -8
  8. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/.gitignore +0 -0
  9. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/LICENSE +0 -0
  10. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/README.md +0 -0
  11. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/agents.md +0 -0
  12. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/cli.md +0 -0
  13. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/commands.md +0 -0
  14. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/context.md +0 -0
  15. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/env.md +0 -0
  16. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/fs.md +0 -0
  17. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/hooks.md +0 -0
  18. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/merge.md +0 -0
  19. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/overlays.md +0 -0
  20. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/reference.md +0 -0
  21. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/resources.md +0 -0
  22. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/scope.md +0 -0
  23. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/skills.md +0 -0
  24. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/sources.md +0 -0
  25. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/api/wrappers.md +0 -0
  26. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/changelog.md +0 -0
  27. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/guide/authoring.md +0 -0
  28. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/guide/install.md +0 -0
  29. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/guide/overlays.md +0 -0
  30. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/guide/private-sync.md +0 -0
  31. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/docs/index.md +0 -0
  32. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/install.py +0 -0
  33. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/mkdocs.yml +0 -0
  34. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/pyproject.toml +0 -0
  35. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/AGENTS.md +0 -0
  36. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/__main__.py +0 -0
  37. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_activate.py +0 -0
  38. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_aos.py +0 -0
  39. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_context.py +0 -0
  40. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_deps.py +0 -0
  41. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_env.py +0 -0
  42. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_fs.py +0 -0
  43. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_hooks.py +0 -0
  44. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_launchers.py +0 -0
  45. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_mcp_gateway.py +0 -0
  46. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_mcp_gateway_clients.py +0 -0
  47. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_mcp_gateway_config.py +0 -0
  48. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_merge.py +0 -0
  49. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/cmds/findings.py +0 -0
  50. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/cmds/launch.py +0 -0
  51. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/hooks/preinvocation_antigravity_context.py +0 -0
  52. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/hooks/pretooluse_codex_env.py +0 -0
  53. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/hooks/sessionstart_codex_context.py +0 -0
  54. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/templates/AGENTS.md +0 -0
  55. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlay/dotagents/templates/PROJECT.md +0 -0
  56. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_overlays.py +0 -0
  57. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_pipwheel.py +0 -0
  58. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_policy.py +0 -0
  59. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_pyvenv.py +0 -0
  60. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_resources.py +0 -0
  61. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_run.py +0 -0
  62. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_scope.py +0 -0
  63. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_skills.py +0 -0
  64. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_sources.py +0 -0
  65. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/_wrappers.py +0 -0
  66. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/__init__.py +0 -0
  67. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/_common.py +0 -0
  68. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/about.py +0 -0
  69. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/build_pyz.py +0 -0
  70. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/context.py +0 -0
  71. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/env.py +0 -0
  72. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/mcp.py +0 -0
  73. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/overlays.py +0 -0
  74. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/cli/path.py +0 -0
  75. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/src/dotagents/py.typed +0 -0
  76. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/_helpers.py +0 -0
  77. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/_shell.py +0 -0
  78. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/README.md +0 -0
  79. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/REVISION +0 -0
  80. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/compose.json +0 -0
  81. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/digest.json +0 -0
  82. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/effective.json +0 -0
  83. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/names.json +0 -0
  84. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/reading.json +0 -0
  85. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/source.json +0 -0
  86. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/aos_vectors/validation.json +0 -0
  87. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/conftest.py +0 -0
  88. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_about.py +0 -0
  89. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_agents.py +0 -0
  90. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_aos_consumer.py +0 -0
  91. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_aos_vectors.py +0 -0
  92. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_api_header.py +0 -0
  93. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_audit_leak.py +0 -0
  94. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_build_pyz.py +0 -0
  95. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_cli_main.py +0 -0
  96. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_cloud_setup.py +0 -0
  97. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_cmds_discovery.py +0 -0
  98. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_context.py +0 -0
  99. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_deps.py +0 -0
  100. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_env.py +0 -0
  101. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_env_cli_scope.py +0 -0
  102. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_env_format.py +0 -0
  103. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_env_into.py +0 -0
  104. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_env_regressions.py +0 -0
  105. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_env_safety.py +0 -0
  106. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_findings_cmd.py +0 -0
  107. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_adapters.py +0 -0
  108. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_cli.py +0 -0
  109. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_env.py +0 -0
  110. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_hooks.py +0 -0
  111. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_init.py +0 -0
  112. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_overlays.py +0 -0
  113. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_packaging.py +0 -0
  114. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_scope_context.py +0 -0
  115. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_fix_skills_adapters.py +0 -0
  116. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_git_ssl_revoke.py +0 -0
  117. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_hooks.py +0 -0
  118. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_init_base.py +0 -0
  119. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_last_mile.py +0 -0
  120. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_launch_cmd.py +0 -0
  121. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_launchers.py +0 -0
  122. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_local_additions.py +0 -0
  123. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_mcp.py +0 -0
  124. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_no_internal_labels.py +0 -0
  125. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_overlay_libs.py +0 -0
  126. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_overlays_cli.py +0 -0
  127. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_overlays_command.py +0 -0
  128. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_path_timeout_frontmatter.py +0 -0
  129. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_pipwheel.py +0 -0
  130. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_pyvenv_core.py +0 -0
  131. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_quoting_injection_safety.py +0 -0
  132. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_scope.py +0 -0
  133. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_sources.py +0 -0
  134. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_sources_uri.py +0 -0
  135. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_wire_hooks.py +0 -0
  136. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tests/test_wrappers.py +0 -0
  137. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tools/audit.py +0 -0
  138. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tools/cloud-setup.sh +0 -0
  139. {dotagents_cli-0.9.0 → dotagents_cli-0.9.1}/tools/pyz_smoke.sh +0 -0
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.1] - 2026-11-06
11
+
12
+ ### Fixed
13
+
14
+ - The Claude Code PowerShell-tool env loader (`init --powershell-env-hook`) no longer
15
+ answers `permissionDecision: "allow"`, which ran calls that would have asked and, under
16
+ `--permission-mode dontAsk` or with no way to ask, calls that would have been refused.
17
+ It now only rewrites the call and the permission system decides. A PowerShell call
18
+ therefore asks unless `PowerShell` itself is allowed, since rules judge the rewritten
19
+ command. An `init --powershell-env-hook` replaces a loader already wired.
20
+
10
21
  ## [0.9.0] - 2026-11-04
11
22
 
12
23
  dotagents implements the [Agent Overlay Specification](https://boriken-dev.github.io/agent-overlay-spec/)
@@ -1743,7 +1754,8 @@ Patch release: the PATH/POSIX-conversion fix above (the only change since 0.3.0)
1743
1754
  affected module sources (`dotagents.cli`, `duho.presets`) to extracted temp files
1744
1755
  before dispatch; a no-op for a plain install.
1745
1756
 
1746
- [Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.9.0...HEAD
1757
+ [Unreleased]: https://github.com/jose-pr/dotagents/compare/v0.9.1...HEAD
1758
+ [0.9.1]: https://github.com/jose-pr/dotagents/compare/v0.9.0...v0.9.1
1747
1759
  [0.9.0]: https://github.com/jose-pr/dotagents/compare/v0.8.3...v0.9.0
1748
1760
  [0.8.3]: https://github.com/jose-pr/dotagents/compare/v0.8.2...v0.8.3
1749
1761
  [0.8.2]: https://github.com/jose-pr/dotagents/compare/v0.8.1...v0.8.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: dotagents-cli
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: The dotagents CLI: install and manage a portable ~/.agents config for AI coding agents
5
5
  Project-URL: Homepage, https://github.com/jose-pr/dotagents/
6
6
  Project-URL: Repository, https://github.com/jose-pr/dotagents
@@ -144,10 +144,12 @@ Claude Code's PowerShell tool never reads `$CLAUDE_ENV_FILE`, so its commands do
144
144
  see the env layers. On Windows, `init --powershell-env-hook` adds a `PreToolUse` hook
145
145
  (matched on the `PowerShell` tool) that prepends the `dotagents env` change set to each
146
146
  PowerShell call (`env --diff --cache`, so an unchanged environment is not reassembled
147
- on every call). **It auto-approves every PowerShell tool call**: rewriting a command
148
- without a prompt on each one requires `permissionDecision: "allow"`, which skips the
149
- approval prompt (your deny and ask rules still apply). It is off by default, and a
150
- plain `init` removes one an earlier release wired.
147
+ on every call). It states no permission decision, so a call asks, is denied or runs as
148
+ your rules say. **Your rules judge the rewritten command**: a narrow allow rule such as
149
+ `PowerShell(Get-Location:*)`, or a command that would pass as read-only, no longer
150
+ matches once the loader leads it, so each PowerShell call asks unless `PowerShell`
151
+ itself is in your allow rules. It is off by default, and a plain `init` removes one an
152
+ earlier release wired.
151
153
 
152
154
  The env half never truncates the file and is guarded by `[ -n "$CLAUDE_ENV_FILE" ]`
153
155
  (see the [hooks docs](https://code.claude.com/docs/en/hooks#persist-environment-variables)):
@@ -1,3 +1,3 @@
1
1
  """dotagents: installable CLI for the dotagents agent-config payload."""
2
2
 
3
- __version__ = "0.9.0"
3
+ __version__ = "0.9.1"
@@ -619,7 +619,8 @@ class ClaudeAgent(Agent):
619
619
  # calls only, so the SessionStart hook never reaches PowerShell tool calls.
620
620
  # Closed via a PreToolUse hook (no matcher -- fires for every tool) that,
621
621
  # for a PowerShell call specifically, prepends a guarded env-loader to that
622
- # call's OWN command via `updatedInput`.
622
+ # call's OWN command via `updatedInput`. It states no `permissionDecision`:
623
+ # "allow" would run what the permission system would have asked about.
623
624
  #
624
625
  # INLINE `-Command`, deliberately NOT a `.ps1` file: a `.ps1` is subject to
625
626
  # PowerShell's script execution policy (AllSigned, or a locked-down
@@ -654,7 +655,7 @@ class ClaudeAgent(Agent):
654
655
  r"""'if (-not $env:AGENTS_RUNTIME_SET) { $env:AGENTS_RUNTIME_SET = "1"; $s = if ($env:AGENTS_HOME) { $env:AGENTS_HOME } else { "$HOME\.agents" }; $c = "$s\bin\dotagents.cmd"; if (-not (Test-Path $c)) { $c = (Get-Command dotagents -ErrorAction SilentlyContinue).Source }; if ($c) { $oe = [Console]::OutputEncoding; [Console]::OutputEncoding = [Text.UTF8Encoding]::new($false); try { & $c env --diff --format powershell --cache 2>$null | Invoke-Expression } finally { [Console]::OutputEncoding = $oe } } }; '; """
655
656
  r'$u = $h.tool_input.PSObject.Copy(); '
656
657
  r'$u.command = $p + $h.tool_input.command; '
657
- r'@{hookSpecificOutput=@{hookEventName="PreToolUse";permissionDecision="allow";updatedInput=$u}} | ConvertTo-Json -Depth 10 -Compress '
658
+ r'@{hookSpecificOutput=@{hookEventName="PreToolUse";updatedInput=$u}} | ConvertTo-Json -Depth 10 -Compress '
658
659
  r'}'
659
660
  )
660
661
 
@@ -757,11 +758,11 @@ class ClaudeAgent(Agent):
757
758
 
758
759
  #: Identity (statusMessage) of the PowerShell PreToolUse env-loader entry.
759
760
  PRETOOLUSE_STATUS = "Checking PowerShell env"
760
- #: Opt-in (`init --powershell-env-hook`): the loader returns
761
- #: `permissionDecision: "allow"` -- the only documented way for PreToolUse
762
- #: to rewrite a command without prompting on every call -- which skips the
763
- #: approval prompt for EVERY PowerShell tool call (deny/ask rules still
764
- #: apply). Off, `init` removes an entry an earlier release wired.
761
+ #: Opt-in (`init --powershell-env-hook`): the loader states no permission
762
+ #: decision, and Claude Code judges the rewritten command, so a PowerShell
763
+ #: call asks unless the tool as a whole is allowed: a narrow allow rule or
764
+ #: a read-only command no longer matches once the loader leads it.
765
+ #: Off, `init` removes an entry an earlier release wired.
765
766
  powershell_env_hook: bool = False
766
767
 
767
768
  def _wire_powershell_pretooluse(self, hooks: dict) -> bool:
@@ -70,8 +70,9 @@ class Init(DotAgentsArgs):
70
70
 
71
71
  powershell_env_hook: bool = False
72
72
  (
73
- "Windows, Claude Code: also wire the PowerShell-tool env loader. It "
74
- "AUTO-APPROVES every PowerShell tool call (skips the permission prompt)."
73
+ "Windows, Claude Code: also wire the PowerShell-tool env loader. Rules then "
74
+ "judge the rewritten command, so a PowerShell call asks unless PowerShell "
75
+ "itself is allowed."
75
76
  )
76
77
  ("--powershell-env-hook",)
77
78
 
@@ -1,12 +1,14 @@
1
1
  """The PowerShell-tool env loader is opt-in (`init --powershell-env-hook`).
2
2
 
3
- It rewrites each PowerShell tool call through PreToolUse `updatedInput`, which
4
- Claude Code documents only together with `permissionDecision: "allow"` (skip
5
- the prompt) or `"ask"` (prompt every call). Wired by default it silently
6
- auto-approved every PowerShell call after `init -g` on Windows.
3
+ It rewrites each PowerShell tool call through PreToolUse `updatedInput` and states no
4
+ permission decision: Claude Code evaluates its rules against the rewritten command and
5
+ asks, denies or runs as it would without a hook. `"allow"` would skip the prompt.
7
6
  """
8
7
 
9
8
  import json
9
+ import os
10
+ import shutil
11
+ import subprocess
10
12
  from pathlib import Path
11
13
 
12
14
  import pytest
@@ -53,10 +55,29 @@ def test_the_wired_line_names_pretooluse_only_when_it_was_wired(tmp_path, caplog
53
55
  assert ("PreToolUse" in wired[0]) is opted_in, wired[0]
54
56
 
55
57
 
56
- def test_the_opted_in_loader_auto_approves_and_says_so():
57
- # Pinned so the auto-approve cannot change silently: the flag's help and
58
- # the docs state it.
59
- assert 'permissionDecision="allow"' in ClaudeAgent.PRETOOLUSE_POWERSHELL_COMMAND
58
+ def test_the_loader_states_no_permission_decision():
59
+ assert "permissionDecision" not in ClaudeAgent.PRETOOLUSE_POWERSHELL_COMMAND
60
+
61
+
62
+ POWERSHELL = shutil.which("powershell")
63
+
64
+
65
+ @pytest.mark.skipif(POWERSHELL is None, reason="Windows PowerShell only")
66
+ def test_the_hook_rewrites_the_call_and_leaves_the_decision_to_the_permission_system(tmp_path):
67
+ call = {"tool_name": "PowerShell", "tool_input": {"command": "Remove-Item x.txt", "description": "d", "timeout": 5}}
68
+ env = {name: value for name, value in os.environ.items() if name.upper() != "AGENTS_RUNTIME_SET"}
69
+ hook = subprocess.run(
70
+ [POWERSHELL, "-NoProfile", "-NonInteractive", "-Command", ClaudeAgent.PRETOOLUSE_POWERSHELL_COMMAND],
71
+ input=json.dumps(call).encode(), env=env, capture_output=True, cwd=tmp_path,
72
+ )
73
+ assert hook.returncode == 0, hook.stderr
74
+ output = json.loads(hook.stdout)["hookSpecificOutput"]
75
+ assert output["hookEventName"] == "PreToolUse"
76
+ assert "permissionDecision" not in output, "a decision here would answer for the permission system"
77
+ updated = output["updatedInput"]
78
+ assert updated["command"].startswith("if (-not $env:AGENTS_RUNTIME_SET)")
79
+ assert updated["command"].endswith("; Remove-Item x.txt")
80
+ assert (updated["description"], updated["timeout"]) == ("d", 5), "the whole input is replaced, so the rest must survive"
60
81
 
61
82
 
62
83
  def test_init_flag_reaches_the_adapter(tmp_path, monkeypatch):
File without changes
File without changes
File without changes
File without changes
File without changes