All Products
Search
Document Center

QwenWork:Hooks

Last Updated:Aug 25, 2026

Hooks let you insert custom logic at key points during QwenWork execution without changing application code. By editing a JSON configuration file, you can:

  • Block dangerous operations before a tool runs.

  • Run lint automatically after a file is written.

  • Show a desktop notification when the Agent finishes.

Unlike prompt instructions, Hooks are deterministic: whenever a configured event fires, the script runs.

Quick Start

The following example blocks commands containing rm -rf.

1. Create the script

mkdir -p ~/.qwenwork/hooks
cat > ~/.qwenwork/hooks/block-rm.sh << 'EOF'
#!/bin/bash
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')

if echo "$command" | grep -q 'rm -rf'; then
  echo "Dangerous command blocked: $command" >&2
  exit 2
fi

exit 0
EOF
chmod +x ~/.qwenwork/hooks/block-rm.sh

2. Add the configuration

Add the following to ~/.qwenwork/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.qwenwork/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}

3. Verify the result

Open QwenWork and ask the Agent to execute a command containing rm -rf. The Hook blocks the command and returns the error message to the Agent.

Configure Hooks

Configuration file

Location

Scope

Description

~/.qwenwork/settings.json

User level

Personal configuration that applies to all QwenWork sessions

Note

Hot reload is not currently supported. Restart QwenWork after changing the configuration file.

Configuration format

{
  "hooks": {
    "EventName": [
      {
        "matcher": "match condition",
        "hooks": [
          {
            "type": "command",
            "command": "command to execute",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

Field

Required

Description

type

Yes

Must be "command"

command

Yes

Shell command to execute

timeout

No

Timeout in seconds; the default is 60

matcher

No

Match condition; omit it to match every event instance

An event can contain multiple matcher groups, and each group can contain multiple Hook commands.

Matcher rules

Pattern

Meaning

Example

Leave empty or "*"

Match all

Run for every tool

Exact value

Exact match

"Bash" matches only the Bash tool

Values separated by |

Match several values

"Write | Edit" matches Write or Edit

Regular expression

Pattern match

"mcp__.*" matches MCP tools

Write a Hook Script

A Hook script receives JSON through stdin and controls behavior using its exit code and stdout. This section describes the input and output shared by all events. Event-specific fields are documented under Hook Events.

Note

QwenWork does not inject environment variables into Hook scripts. Read the session ID, working directory, tool information, and other event data from the stdin JSON payload.

Input

All events include the following common fields:

Field

Description

session_id

Current session ID

cwd

Current working directory

hook_event_name

Name of the event that fired

Different events add their own fields to this payload. Use jq to parse the input:

#!/bin/bash
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

Output

Exit code 0 indicates success. Exit code 2 blocks supported events and injects stderr into the conversation. Other exit codes are treated as non-blocking errors.

When a script exits with 0, supported events can parse JSON from stdout for more precise control. Stdout is ignored when the exit code is not 0.

Hook Events

SessionStart

Fires when a session starts.

Matcher: session source

Matcher value

Scenario

startup

Start a new session

resume

Resume an existing session

compact

Context compaction has completed

{
  "source": "startup",
  "model": "Auto"
}

SessionEnd

Fires when a session ends.

Matcher: end reason

  • prompt_input_exit: The user exits input, for example with Ctrl+D.

  • other: Any other reason.

{
  "reason": "prompt_input_exit"
}

UserPromptSubmit

Fires after the user submits a prompt and before the Agent starts processing it.

{
  "prompt": "Write a sorting function"
}

PreToolUse

Fires before a tool runs and can block the tool call.

Matcher: tool name, such as Bash, Write, Edit, Read, Glob, or Grep. MCP tools use names such as mcp__server__tool.

{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf /tmp/build"},
  "tool_use_id": "toolu_01ABC123"
}

To block the tool, exit with code 2. Stderr is returned to the Agent as an error.

PostToolUse

Fires after a tool completes successfully.

Matcher: tool name

{
  "tool_name": "Write",
  "tool_input": {"file_path": "/path/to/file.ts", "content": "..."},
  "tool_response": "File written successfully",
  "tool_use_id": "toolu_01ABC123"
}

PostToolUseFailure

Fires after a tool fails.

Matcher: tool name

{
  "tool_name": "Bash",
  "tool_input": {"command": "npm test"},
  "tool_use_id": "toolu_01ABC123",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false
}

Stop

Fires when the main Agent finishes a response and has no pending tool calls. The Hook can prevent the Agent from stopping so it continues working.

To prevent the Agent from stopping, exit with code 2. Stderr is injected into the conversation as a message and the Agent continues.

SubagentStart / SubagentStop

Fire when a sub-agent starts or finishes. Like Stop, SubagentStop can prevent a sub-agent from stopping.

Matcher: Agent type

{
  "agent_id": "a1b2c3d4",
  "agent_type": "task"
}

PreCompact

Fires before context compaction.

Matcher: trigger type

Matcher value

Scenario

manual

The user runs /compact manually

auto

The context window is full and compaction starts automatically

{
  "trigger": "manual",
  "custom_instructions": "Preserve all tool results"
}

Notification

Fires for notifications such as permission requests and completed tasks.

Matcher: notification type

  • permission: Permission request notification.

  • result: Agent result notification.

{
  "message": "Agent is requesting permission to run: rm -rf node_modules",
  "title": "Permission Required",
  "notification_type": "permission"
}

PermissionRequest

Fires when a tool needs user authorization.

Matcher: tool name

{
  "tool_name": "Bash",
  "tool_input": {"command": "rm -rf node_modules"}
}

Use Cases

Desktop notifications

Show a desktop notification when the Agent completes a task or needs authorization.

Create ~/.qwenwork/hooks/notify.sh on macOS:

#!/bin/bash
input=$(cat)
message=$(echo "$input" | jq -r '.message')

if echo "$message" | grep -q "^Agent"; then
  osascript -e 'display notification "Task completed" with title "QwenWork"'
else
  osascript -e 'display notification "Authorization required" with title "QwenWork"'
fi

exit 0

Configure the Hook:

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.qwenwork/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}

Run lint after file changes

Run lint automatically whenever the Agent writes or edits a file.

Create ${project}/.qwenwork/hooks/auto-lint.sh:

#!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')

case "$file_path" in
  *.js|*.ts|*.jsx|*.tsx)
    npx eslint "$file_path" --fix 2>/dev/null
    ;;
esac

exit 0

Configure the PostToolUse event with matcher Write|Edit and command .qwenwork/hooks/auto-lint.sh.

Ask the Agent to continue

When the Agent tries to stop, check for unfinished work and inject a message that tells it to continue.

Create ~/.qwenwork/hooks/check-continue.sh:

#!/bin/bash
if [ -n "$(git status --porcelain 2>/dev/null)" ]; then
  echo "Uncommitted changes detected. Complete the git commit." >&2
  exit 2
fi

exit 0

Configure the Stop event with command ~/.qwenwork/hooks/check-continue.sh.