preToolUse hook returning 'allow' should short-circuit built-in tool dialogs like exit_plan_mode
- Linguagem predominante
- Shell
- Estrelas
- 11.2k
- Forks
- 1.9k
- Merge médio
- 14h 16min
- PRs com merge (30d)
- 6
Descrição
## Summary
When a `preToolUse` hook returns `permissionDecision: "allow"` for the `exit_plan_mode` tool, the built-in plan approval dialog still appears, requiring the user to manually click through it. This makes it impossible for hooks to fully automate plan approval, even though `"deny"` already fully short-circuits tool execution.
## Current behavior
1. `preToolUse` hook fires for `exit_plan_mode`
2. Hook returns `{ "permissionDecision": "allow" }` -- this only answers "yes, this tool may run"
3. The tool executes and calls `requestExitPlanMode()` internally
4. The plan approval dialog (`exit_plan_mode.requested`) appears anyway
5. User must manually approve a second time
`"deny"` works perfectly -- the tool never executes, no dialog appears. But `"allow"` is effectively a no-op for tools that have their own built-in interaction dialogs.
## Use case
I build [Plannotator](https://github.com/ramos/plannotator), a visual plan review UI for agentic coding tools. My `preToolUse` hook intercepts `exit_plan_mode`, opens a rich review interface, collects the user's decision, and returns `"allow"` or `"deny"` with feedback. The deny path works great. But on approval, users still have to click through the CLI's plan dialog -- making the approval flow feel redundant.
Hook config:
```json
{
"hooks": {
"preToolUse": [
{
"type": "command",
"bash": "$HOME/.local/bin/plannotator copilot-plan",
"timeoutSec": 345600
}
]
}
}
```
## Proposed solution
Allow `preToolUse` hooks returning `"allow"` for tools with built-in dialogs (like `exit_plan_mode`) to supply the dialog response, skipping the dialog entirely. For example:
```json
{
"permissionDecision": "allow",
"exitPlanModeResponse": {
"approved": true,
"selectedAction": "interactive",
"autoApproveEdits": false
}
}
```
Alternatively, any of these would also work:
- A dedicated `preExitPlanMode` hook type in `QueryHooks` with `ExitPlanModeResponse` as its output type
- A general `preInteraction` hook that fires before any blocking dialog on the `InteractionManager`
- Treating `preToolUse` `"allow"` on `exit_plan_mode` as implicit plan approval (using defaults for `selectedAction`, etc.)
## Why this matters
The current behavior is asymmetric: `"deny"` gives hooks full control, but `"allow"` still forces a manual step. Extensions that implement their own review UI (with richer context, annotations, sharing, etc.) cannot fully replace the built-in dialog on the approval path. This limits the extensibility that the hook system otherwise enables well.
For provenance purposes, this issue was AI assisted.
- not spam though, also the person who authored [this issue](https://github.com/anthropics/claude-code/issues/712).
Guia de contribuição
Direção de pesquisa
Start by tracing preToolUse handling for exit_plan_mode and the requestExitPlanMode() path, including the InteractionManager mentioned in the proposed alternatives. Determine how an allowed hook could provide an ExitPlanModeResponse and verify that approval skips the built-in plan dialog while denial remains unchanged.
Escrita pelo modelo de indexação a partir do texto da issue.
Avaliação
- Stack de tecnologia
- shell
- Domínio
- cli
- Tipo de issue
- Funcionalidade
- Dificuldade
- 4/5
- Tempo estimado
- 3-5 dias
- Status de atividade
- Pouca atividade
- Clareza
- Razoavelmente clara
- Facilidade para iniciantes
- 48/100