# Start every Claude Code session with your replies

> A SessionStart hook that tells Claude Code which Taskhold task is in progress and what you changed since, so a new session picks up your replies first.

Source: https://taskhold.com/docs/claude-code-hooks
Last updated: 2026-10-26

A new Claude Code session doesn't know you answered a blocked question an hour ago. A SessionStart hook can tell it: Claude Code adds the hook's output to the session as context before the first prompt. This one prints the task in progress and the last 10 changes you made in Taskhold.

## Add the hook script

Save this as ~/.claude/hooks/taskhold-session-start.sh and make it executable. It reads the key the same way as the status line recipe:

```bash
#!/bin/bash
# ~/.claude/hooks/taskhold-session-start.sh: what's in progress, and what the person changed.
# Claude Code adds this script's output to the session as context. Needs curl and jq.
key=${TASKHOLD_API_KEY:-$(cat ~/.config/taskhold/key 2>/dev/null)}
base=${TASKHOLD_API_URL:-https://taskhold.com/api}
[ -z "$key" ] && exit 0
auth="Authorization: Bearer $key"
current=$(curl -s --max-time 3 "$base/tasks/current" -H "$auth" | jq -r '.task // empty
  | "Taskhold: \"\(.title)\" is in progress (id \(.id)), \(.overall_progress // 0)% done."')
changes=$(curl -s --max-time 3 "$base/events?actor=human&limit=10" -H "$auth" | jq -r '.events[]?
  | "- \(.created_at[0:16]) \(.task_title // "list"): \(.summary)"')
[ -n "$current" ] && echo "$current"
[ -n "$changes" ] && printf 'Latest changes the person made in Taskhold:\n%s\nCall taskhold_updates for replies since your last cursor.\n' "$changes"
exit 0
```

## Register it

Add the hook to ~/.claude/settings.json (all projects) or .claude/settings.json (one repo):

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/taskhold-session-start.sh" }
        ]
      }
    ]
  }
}
```

SessionStart runs before MCP servers connect, so the hook uses the REST API, not the MCP tools. Keep it fast: both requests time out after 3 seconds.

## What Claude sees

```text
Taskhold: "Deploy v2.4 to staging" is in progress (id fd11278a-...), 33% done.
Latest changes the person made in Taskhold:
- 2026-10-26T05:30 Deploy v2.4 to staging: Use the shared Stripe test account, not the new one.
Call taskhold_updates for replies since your last cursor.
```

## Show on the board when Claude Code waits for you

Claude Code fires a Notification hook when it needs your permission for a tool or has been idle at the prompt. Those waits happen in the terminal, so the board can't see them. This hook copies the wait onto the task in progress as its latest message, without moving the bar. Save it as ~/.claude/hooks/taskhold-waiting.sh and make it executable:

```bash
#!/bin/bash
# ~/.claude/hooks/taskhold-waiting.sh: when Claude Code stops to wait for you (a permission
# prompt, or idle at the prompt), say so on the live board under the task in progress.
# Claude Code sends the hook JSON on stdin; .message is the text it shows. Needs curl and jq.
key=${TASKHOLD_API_KEY:-$(cat ~/.config/taskhold/key 2>/dev/null)}
base=${TASKHOLD_API_URL:-https://taskhold.com/api}
[ -z "$key" ] && exit 0
msg=$(jq -r '.message // "Waiting for input"' | cut -c1-200)
auth="Authorization: Bearer $key"
id=$(curl -s --max-time 3 "$base/tasks/current" -H "$auth" | jq -r '.task.id // empty')
[ -z "$id" ] && exit 0
jq -n --arg m "In the terminal: $msg" '{message: $m}' | curl -s --max-time 3 -o /dev/null \
  "$base/tasks/$id/progress" -H "$auth" -H "Content-Type: application/json" \
  -H "X-Taskhold-Agent: Claude Code" -d @-
exit 0
```

Register it next to the SessionStart hook:

```json
{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/taskhold-waiting.sh" }
        ]
      }
    ]
  }
}
```

The card then reads "In the terminal: Claude needs your permission to use Bash" until the agent reports again. For questions that need an answer, the agent should still call taskhold_block, which puts the task under Needs you and lets you reply from the board.

## Related

- [Show the current task in the Claude Code status line](https://taskhold.com/docs/claude-code-status-line.md)
- [Taskhold events API and Server-Sent Events stream](https://taskhold.com/docs/events.md)
- [How agents should write progress notes and questions](https://taskhold.com/docs/agent-messages.md)
